From f7b7c1bf37fcf5431b8f54490586164c57045828 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Tue, 17 Feb 2026 00:07:37 +0100 Subject: [PATCH 01/68] add common php option to example --- nix/image/bootstrap.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/nix/image/bootstrap.sh b/nix/image/bootstrap.sh index 141affa..f7827db 100755 --- a/nix/image/bootstrap.sh +++ b/nix/image/bootstrap.sh @@ -5,6 +5,8 @@ touch /var/log/nginx/error.log touch /var/log/cron/owncloud.log echo "# Options in here overwrite the builtin php.ini" > /php.ini +echo "# xdebug.mode = debug" >> /php.ini +echo "# xdebug.start_with_request = yes" >> /php.ini chmod 0777 /php.ini PHP_INI_DIR="$(php --ini | grep 'Scan' | cut -d ' ' -f7)" ln -s /php.ini "$PHP_INI_DIR/zz_extra.ini" From e3161f34aee847899d5667c2253f6d28d5307c74 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Tue, 24 Feb 2026 20:33:43 +0100 Subject: [PATCH 02/68] 127.0.0.1 now works for proxy --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2a12c61..10e133c 100644 --- a/README.md +++ b/README.md @@ -273,7 +273,7 @@ proxy to allow using a wildcard domain. ### Setup - Set a DNS record for `*.haze.exmaple.com` and `haze.example.com` pointing to - your development machine. (127.0.0.1 will not work) + `127.0.0.1` or your development machine if you need to access it from other devices. - Set the `proxy` configuration with your domain and desired listen endpoint - Setup a service to run `haze proxy` in the background as your own user. A systemd user service is recommended. From 4f6b1fbd9b8afdde5d66a4804f3e31ce46dd4945 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Tue, 24 Feb 2026 20:38:04 +0100 Subject: [PATCH 03/68] readme tweaks and example user service --- README.md | 50 +++++++++++++++++++++++++------------------------- haze.service | 10 ++++++++++ 2 files changed, 35 insertions(+), 25 deletions(-) create mode 100644 haze.service diff --git a/README.md b/README.md index 10e133c..012d63d 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Easy setup and management of Nextcloud test instances using docker ## What -`haze` provides an easy way to setup Nextcloud test instances with a choice of +`haze` provides an easy way to set up Nextcloud test instances with a choice of php version, database server, optional s3 or ldap setup and more. ## Setup @@ -71,29 +71,29 @@ mode and can be used for debugging php itself with gdb. Additionally, you can use the following options when starting an instance: -- `s3`: setup an S3 server and configure to Nextcloud to use it as primary +- `s3`: set up an S3 server and configure to Nextcloud to use it as primary storage. - ``: by specifying the path to an app package this package will be extracted into the apps. directory of the new instance (overwriting any existing app code). This can be used to quickly test a packaged app. -- `ldap`: setup an LDAP server. -- `office`: setup a Nextcloud Office server. +- `ldap`: set up an LDAP server. +- `office`: set up a Nextcloud Office server. - `onlyoffice` setup an onlyoffice document server. -- `push` setup [client push](https://github.com/nextcloud/notify_push). -- `smb`: setup a samba server for external storage use. -- `dav`: setup a WebDAV server for external storage use. -- `sftp`: setup a SFTP server for external storage use. -- `kaspersky`: setup a kaspersky scan engine server in http mode. ( Requires +- `push` set up [client push](https://github.com/nextcloud/notify_push). +- `smb`: set up a samba server for external storage use. +- `dav`: set up a WebDAV server for external storage use. +- `sftp`: set up a SFTP server for external storage use. +- `kaspersky`: set up a kaspersky scan engine server in http mode. ( Requires [manually setting up the image](https://github.com/icewind1991/kaspersky-docker)) - `kaspersky-icap`: setup a kaspersky scan engine server in ICAP mode. -- `clamav`: setup a local clam av scanner in executable mode. -- `clamav-socket`: setup a clam av scanner in socket mode. -- `clamav-icap`: setup a clam av scanner in ICAP mode. -- `clamav-icap-tls`: setup a clam av scanner in ICAP mode with TLS encryption. +- `clamav`: set up a local clam av scanner in executable mode. +- `clamav-socket`: set up a clam av scanner in socket mode. +- `clamav-icap`: set up a clam av scanner in ICAP mode. +- `clamav-icap-tls`: set up a clam av scanner in ICAP mode with TLS encryption. - `oc`: start an ownCloud instance in the same network. - `imaginary`: start an Imaginary service and configure it for preview generation. -- `mail`: start an [smtp4dev](https://github.com/rnwood/smtp4dev) server and +- `mail`: start a [smtp4dev](https://github.com/rnwood/smtp4dev) server and configure it the mail server. - `redis`: start a separate container for redis. - `redis-tls`: connect to redis over TLS. @@ -105,7 +105,7 @@ Additionally, you can use the following options when starting an instance: haze test [database] [php-version] [path] ``` -Where `path` is a file or folder to run phpunit in, relative to the sources +Where `path` is a file or folder to run PHPUnit in, relative to the sources root. ### List running instances @@ -129,7 +129,7 @@ haze clean ## Controlling running instances The following commands run against the most recently started instance and allow -optionally providing a `match` to select a specific instance by it's name. +optionally providing a `match` to select a specific instance by its name. #### Open an instance @@ -206,7 +206,7 @@ haze [match] env [args] Runs the provided command with `NEXTCLOUD_URL`, `DATABASE_URL` and `REDIS_URL` environment variables set for the matched instance. -This is intented to run a local +This is intended to run a local [push daemon](https://github.com/nextcloud/notify_push) against an instance. #### Update the container images @@ -254,19 +254,19 @@ Performs a pull in all git repositories within the apps folder. Multiple instances can reach each other by using their instance name as domain name to allow for testing federation between instances. Alternatively, you can -setup the haze proxy and the proxied domains to get https support between +set up the haze proxy and the proxied domains to get https support between instances. ## Proxy By default, instances can be accessed by their IP. In order to get more -memorable urls and allow supporting https, haze comes with a builtin reverse +memorable URLs and allow supporting https, haze comes with a builtin reverse proxy to allow using a wildcard domain. ### Requirements - A domain name you can set wildcard DNS records for -- A reverse proxy like nginx or apache +- A reverse proxy like nginx or Apache - (optionally) a wildcard ssl certificate (can be acquiring using letsencrypt and dns verification) @@ -274,9 +274,9 @@ proxy to allow using a wildcard domain. - Set a DNS record for `*.haze.exmaple.com` and `haze.example.com` pointing to `127.0.0.1` or your development machine if you need to access it from other devices. -- Set the `proxy` configuration with your domain and desired listen endpoint -- Setup a service to run `haze proxy` in the background as your own user. A - systemd user service is recommended. +- Set the `proxy` configuration with your domain and desired listen endpoint. +- Set up a service to run `haze proxy` in the background as your own user. A + systemd user service is recommended (see [haze.service](./haze.service) for an example). - Configure your reverse proxy of choice to proxy `*.haze.example.com` and `haze.example.com` to the proxy's listen endpoint - (optional) acquire a wildcard ssl certificate for your domain and set your @@ -286,7 +286,7 @@ proxy to allow using a wildcard domain. ### Usage -When the proxy is configured, generated urls for the instances will use a +When the proxy is configured, generated URLs for the instances will use a subdomain of the configured domain, e.g. the `rolling-bees` instance will be available at `rolling-bees.haze.example.com`. Additionally, `haze.example.com` will automatically point to the last created instance. @@ -322,7 +322,7 @@ read_only = true [proxy] # optional address = "haze.example.com" # base domain -https = true # Is the proxy behind an https terminating proxy +https = true # Is the proxy behind a https terminating proxy listen = "/run/haze/haze.sock" # either a unix socket path #listen = "127.0.0.1:8080" # or a socket address diff --git a/haze.service b/haze.service new file mode 100644 index 0000000..4b0ff66 --- /dev/null +++ b/haze.service @@ -0,0 +1,10 @@ +[Install] +WantedBy=default.target + +[Service] +ExecStart=/usr/bin/haze proxy +Restart=on-failure +RestartSec=10 + +[Unit] +Description=Haze reverse proxy \ No newline at end of file From cead37fae7da464560c7aba535c51b7f234f2a2b Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Wed, 25 Feb 2026 19:29:55 +0100 Subject: [PATCH 04/68] fix custom php.ini, again --- README.md | 8 +++++--- nix/image/bootstrap.sh | 10 +++++----- src/args.rs | 2 +- src/mapping.rs | 2 +- 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 012d63d..82e8bd7 100644 --- a/README.md +++ b/README.md @@ -227,7 +227,7 @@ haze [match] edit haze [match] reload ``` -The php configuration can edit changed with `haze edit /php.ini` +The php configuration can edit changed with `haze edit /config/php.ini` #### Checkout a branch for all local apps @@ -273,10 +273,12 @@ proxy to allow using a wildcard domain. ### Setup - Set a DNS record for `*.haze.exmaple.com` and `haze.example.com` pointing to - `127.0.0.1` or your development machine if you need to access it from other devices. + `127.0.0.1` or your development machine if you need to access it from other + devices. - Set the `proxy` configuration with your domain and desired listen endpoint. - Set up a service to run `haze proxy` in the background as your own user. A - systemd user service is recommended (see [haze.service](./haze.service) for an example). + systemd user service is recommended (see [haze.service](./haze.service) for an + example). - Configure your reverse proxy of choice to proxy `*.haze.example.com` and `haze.example.com` to the proxy's listen endpoint - (optional) acquire a wildcard ssl certificate for your domain and set your diff --git a/nix/image/bootstrap.sh b/nix/image/bootstrap.sh index f7827db..5705b2c 100755 --- a/nix/image/bootstrap.sh +++ b/nix/image/bootstrap.sh @@ -4,12 +4,12 @@ touch /var/log/nginx/access.log touch /var/log/nginx/error.log touch /var/log/cron/owncloud.log -echo "# Options in here overwrite the builtin php.ini" > /php.ini -echo "# xdebug.mode = debug" >> /php.ini -echo "# xdebug.start_with_request = yes" >> /php.ini -chmod 0777 /php.ini +echo "# Options in here overwrite the builtin php.ini" > /config/php.ini +echo "# xdebug.mode = debug" >> /config/php.ini +echo "# xdebug.start_with_request = yes" >> /config/php.ini +chmod 0777 /config/php.ini PHP_INI_DIR="$(php --ini | grep 'Scan' | cut -d ' ' -f7)" -ln -s /php.ini "$PHP_INI_DIR/zz_extra.ini" +ln -s /config/php.ini "$PHP_INI_DIR/zz_extra.ini" HAZE_UID=${HAZE_UID:-www-data} HAZE_GID=${HAZE_GID:-www-data} diff --git a/src/args.rs b/src/args.rs index bcf9365..f5434ca 100644 --- a/src/args.rs +++ b/src/args.rs @@ -469,7 +469,7 @@ pub enum HazeCommand { Edit, /// Reload the php configuration in the instance #[strum(props( - Details = "note: you can overwrite php.ini settings with haze [filter] edit /php.ini" + Details = "note: you can overwrite php.ini settings with haze [filter] edit /config/php.ini" ))] Reload, } diff --git a/src/mapping.rs b/src/mapping.rs index f2a93a3..b224f62 100644 --- a/src/mapping.rs +++ b/src/mapping.rs @@ -161,7 +161,7 @@ pub fn default_mappings<'a>() -> impl IntoIterator> { .dont_create(), Mapping::new(WorkDir, "xdebug", "/tmp/xdebug"), Mapping::new(WorkDir, "profiling", "/tmp/profiling"), - Mapping::new(WorkDir, "php.ini", "/php.ini").file(), + Mapping::new(WorkDir, "php-config", "/config"), ]; IntoIterator::into_iter(mappings) } From 8941c697fbb3af10042dad018bd1f4ba5f5ee668 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Fri, 27 Feb 2026 17:07:37 +0100 Subject: [PATCH 05/68] fix `integration` not using all passed options --- nix/image/bootstrap.sh | 1 + src/main.rs | 22 ++-------------------- 2 files changed, 3 insertions(+), 20 deletions(-) diff --git a/nix/image/bootstrap.sh b/nix/image/bootstrap.sh index 5705b2c..5f85777 100755 --- a/nix/image/bootstrap.sh +++ b/nix/image/bootstrap.sh @@ -4,6 +4,7 @@ touch /var/log/nginx/access.log touch /var/log/nginx/error.log touch /var/log/cron/owncloud.log +mkdir -p /config echo "# Options in here overwrite the builtin php.ini" > /config/php.ini echo "# xdebug.mode = debug" >> /config/php.ini echo "# xdebug.start_with_request = yes" >> /config/php.ini diff --git a/src/main.rs b/src/main.rs index 091afd0..485867c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -269,26 +269,8 @@ async fn main() -> Result { return Ok(result.into()); } HazeArgs::Integration { options, mut args } => { - let cloud = Cloud::create(&docker, options, &config).await?; - println!("Waiting for servers to start"); - cloud.wait_for_start(&docker).await?; - println!("Installing"); - if let Err(e) = cloud - .exec( - &docker, - vec![ - "install", - &config.auto_setup.username, - &config.auto_setup.password, - ], - false, - Vec::::default(), - ) - .await - { - cloud.destroy(&docker).await?; - return Err(e); - } + let cloud = setup(&docker, options, &config).await?; + args.insert(0, "integration".to_string()); cloud.exec(&docker, args, false, get_forward_env()).await?; cloud.destroy(&docker).await?; From f569ca17e2e9c4cb5f90779e72cb2f5046b84cca Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Fri, 27 Feb 2026 22:01:44 +0100 Subject: [PATCH 06/68] allow using release sources --- Cargo.lock | 1104 ++++++++++++++++++++++++++++++++++++++++++++---- Cargo.toml | 8 +- README.md | 5 +- src/cloud.rs | 25 +- src/help.rs | 1 + src/main.rs | 9 +- src/mapping.rs | 13 +- src/php.rs | 20 +- src/sources.rs | 124 ++++++ 9 files changed, 1211 insertions(+), 98 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4cfe4f8..ce6684d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,17 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + [[package]] name = "android-tzdata" version = "0.1.1" @@ -96,7 +107,7 @@ checksum = "d556ec1359574147ec0c4fc5eb525f3f23263a592b1a9c07e0a75b427de55c97" dependencies = [ "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.117", ] [[package]] @@ -200,6 +211,12 @@ dependencies = [ "backtrace", ] +[[package]] +name = "base16ct" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd307490d624467aa6f74b0eabb77633d1f758a7b25f12bceb0b22e08d9726f6" + [[package]] name = "base64" version = "0.22.1" @@ -212,6 +229,24 @@ version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block-buffer" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdd35008169921d80bc60d3d0ab416eecb028c4cd653352907921d95084790be" +dependencies = [ + "hybrid-array", +] + [[package]] name = "bollard" version = "0.18.1" @@ -269,9 +304,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.17.0" +version = "3.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" +checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" [[package]] name = "byteorder" @@ -285,6 +320,15 @@ version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f61dac84819c6588b558454b194026eb1f09c293b9036ae9b159e74e73ab6cf9" +[[package]] +name = "bzip2" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3a53fac24f34a81bc9954b5d6cfce0c21e18ec6959f44f56e8e90e4bb7c346c" +dependencies = [ + "libbz2-rs-sys", +] + [[package]] name = "camino" version = "1.1.9" @@ -311,6 +355,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + [[package]] name = "chrono" version = "0.4.40" @@ -321,7 +371,17 @@ dependencies = [ "iana-time-zone", "num-traits", "serde", - "windows-link", + "windows-link 0.1.0", +] + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common 0.1.7", + "inout", ] [[package]] @@ -355,7 +415,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.117", ] [[package]] @@ -370,6 +430,41 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" +[[package]] +name = "console" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03e45a4a8926227e4197636ba97a9fc9b00477e9f4bd711395687c5f0734bec4" +dependencies = [ + "encode_unicode", + "libc", + "once_cell", + "unicode-width 0.2.0", + "windows-sys 0.61.2", +] + +[[package]] +name = "const-oid" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c" + +[[package]] +name = "constant_time_eq" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d52eff69cd5e647efe296129160853a42795992097e8af39800e1060caeea9b" + +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.7" @@ -377,14 +472,42 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] -name = "crc32fast" -version = "1.4.2" +name = "cpufeatures" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" dependencies = [ "cfg-if", ] +[[package]] +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "crypto-common" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77727bb15fa921304124b128af125e7e3b968275d1b108b379190264f4423710" +dependencies = [ + "hybrid-array", +] + [[package]] name = "dbus" version = "0.9.7" @@ -397,13 +520,41 @@ dependencies = [ ] [[package]] -name = "deranged" -version = "0.3.11" +name = "deflate64" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +checksum = "807800ff3288b621186fe0a8f3392c4652068257302709c24efd918c3dffcdc2" + +[[package]] +name = "deranged" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" dependencies = [ "powerfmt", - "serde", + "serde_core", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer 0.10.4", + "crypto-common 0.1.7", + "subtle", +] + +[[package]] +name = "digest" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "285743a676ccb6b3e116bc14cc69319b957867930ae9c4822f8e0f54509d7243" +dependencies = [ + "block-buffer 0.12.0", + "const-oid", + "crypto-common 0.2.1", ] [[package]] @@ -435,7 +586,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.117", ] [[package]] @@ -444,6 +595,12 @@ version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7914353092ddf589ad78f25c5c1c21b7f80b0ff8621e7c814c3485b5306da9d" +[[package]] +name = "encode_unicode" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" + [[package]] name = "enum_dispatch" version = "0.3.13" @@ -453,7 +610,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.117", ] [[package]] @@ -491,6 +648,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11faaf5a5236997af9848be0bef4db95824b1d534ebc64d0f0c6cf3e67bd38dc" dependencies = [ "crc32fast", + "libz-rs-sys", "miniz_oxide", ] @@ -500,6 +658,12 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + [[package]] name = "form_urlencoded" version = "1.2.1" @@ -532,7 +696,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.117", ] [[package]] @@ -561,6 +725,16 @@ dependencies = [ "slab", ] +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + [[package]] name = "getrandom" version = "0.2.15" @@ -568,8 +742,39 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "r-efi", + "wasip2", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "139ef39800118c7683f2fd3c98c1b23c09ae076556b435f8e9064ae108aaeeec" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "r-efi", + "wasip2", + "wasip3", + "wasm-bindgen", ] [[package]] @@ -603,7 +808,7 @@ dependencies = [ "futures-core", "futures-sink", "http", - "indexmap 2.7.1", + "indexmap 2.13.0", "slab", "tokio", "tokio-util", @@ -621,6 +826,15 @@ name = "hashbrown" version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +dependencies = [ + "foldhash", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" [[package]] name = "haze" @@ -629,6 +843,7 @@ dependencies = [ "async-trait", "atty", "axum", + "base16ct", "bollard", "camino", "directories-next", @@ -639,6 +854,7 @@ dependencies = [ "hyper", "hyper-reverse-proxy", "hyper-util", + "indicatif", "itertools", "local-ip-address", "maplit", @@ -649,6 +865,7 @@ dependencies = [ "reqwest", "serde", "serde_json", + "sha2 0.11.0-rc.5", "shell-words", "strum", "tar", @@ -657,6 +874,7 @@ dependencies = [ "toml", "tracing", "tracing-subscriber", + "zip", ] [[package]] @@ -686,6 +904,15 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.7", +] + [[package]] name = "http" version = "1.2.0" @@ -732,6 +959,15 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" +[[package]] +name = "hybrid-array" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1b229d73f5803b562cc26e4da0396c8610a4ee209f4fac8fa4f8d709166dc45" +dependencies = [ + "typenum", +] + [[package]] name = "hyper" version = "1.6.0" @@ -779,6 +1015,23 @@ dependencies = [ "tracing", ] +[[package]] +name = "hyper-rustls" +version = "0.27.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" +dependencies = [ + "http", + "hyper", + "hyper-util", + "rustls", + "rustls-native-certs", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", +] + [[package]] name = "hyper-util" version = "0.1.10" @@ -951,9 +1204,15 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.117", ] +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + [[package]] name = "idna" version = "1.0.3" @@ -988,13 +1247,36 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.7.1" +version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" dependencies = [ "equivalent", - "hashbrown 0.15.2", + "hashbrown 0.16.1", "serde", + "serde_core", +] + +[[package]] +name = "indicatif" +version = "0.18.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25470f23803092da7d239834776d653104d551bc4d7eacaf31e6837854b8e9eb" +dependencies = [ + "console", + "portable-atomic", + "unicode-width 0.2.0", + "unit-prefix", + "web-time", +] + +[[package]] +name = "inout" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" +dependencies = [ + "generic-array", ] [[package]] @@ -1066,6 +1348,18 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + +[[package]] +name = "libbz2-rs-sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c4a545a15244c7d945065b5d392b2d2d7f21526fba56ce51467b06ed445e8f7" + [[package]] name = "libc" version = "0.2.170" @@ -1105,6 +1399,15 @@ dependencies = [ "redox_syscall", ] +[[package]] +name = "libz-rs-sys" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "902bc563b5d65ad9bba616b490842ef0651066a1a1dc3ce1087113ffcb873c8d" +dependencies = [ + "zlib-rs", +] + [[package]] name = "libz-sys" version = "1.1.21" @@ -1143,9 +1446,24 @@ dependencies = [ [[package]] name = "log" -version = "0.4.26" +version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "lru-slab" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" + +[[package]] +name = "lzma-rust2" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47bb1e988e6fb779cf720ad431242d3f03167c1b3f2b1aae7f1a94b2495b36ae" +dependencies = [ + "sha2 0.10.9", +] [[package]] name = "maplit" @@ -1193,7 +1511,7 @@ checksum = "bf45bf44ab49be92fd1227a3be6fc6f617f1a337c06af54981048574d8783147" dependencies = [ "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.117", ] [[package]] @@ -1268,9 +1586,9 @@ dependencies = [ [[package]] name = "num-conv" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" +checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050" [[package]] name = "num-traits" @@ -1314,6 +1632,12 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "openssl-probe" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" + [[package]] name = "overload" version = "0.1.1" @@ -1330,6 +1654,16 @@ dependencies = [ "supports-color 3.0.2", ] +[[package]] +name = "pbkdf2" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" +dependencies = [ + "digest 0.10.7", + "hmac", +] + [[package]] name = "percent-encoding" version = "2.3.1" @@ -1347,7 +1681,7 @@ dependencies = [ "itertools", "proc-macro2", "quote", - "rand", + "rand 0.8.5", ] [[package]] @@ -1368,12 +1702,24 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" +[[package]] +name = "portable-atomic" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" + [[package]] name = "powerfmt" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" +[[package]] +name = "ppmd-rust" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efca4c95a19a79d1c98f791f10aebd5c1363b473244630bb7dbde1dc98455a24" + [[package]] name = "ppv-lite86" version = "0.2.20" @@ -1383,6 +1729,16 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn 2.0.117", +] + [[package]] name = "proc-macro2" version = "1.0.94" @@ -1392,6 +1748,61 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "quinn" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" +dependencies = [ + "bytes", + "cfg_aliases", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls", + "socket2", + "thiserror 2.0.12", + "tokio", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-proto" +version = "0.11.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31" +dependencies = [ + "bytes", + "getrandom 0.3.4", + "lru-slab", + "rand 0.9.2", + "ring", + "rustc-hash", + "rustls", + "rustls-pki-types", + "slab", + "thiserror 2.0.12", + "tinyvec", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-udp" +version = "0.5.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" +dependencies = [ + "cfg_aliases", + "libc", + "once_cell", + "socket2", + "tracing", + "windows-sys 0.59.0", +] + [[package]] name = "quote" version = "1.0.39" @@ -1401,6 +1812,12 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + [[package]] name = "rand" version = "0.8.5" @@ -1408,8 +1825,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha", - "rand_core", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.5", ] [[package]] @@ -1419,7 +1846,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.5", ] [[package]] @@ -1428,7 +1865,16 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom", + "getrandom 0.2.15", +] + +[[package]] +name = "rand_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" +dependencies = [ + "getrandom 0.3.4", ] [[package]] @@ -1452,7 +1898,7 @@ version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" dependencies = [ - "getrandom", + "getrandom 0.2.15", "libredox", "thiserror 1.0.69", ] @@ -1477,6 +1923,7 @@ dependencies = [ "http-body", "http-body-util", "hyper", + "hyper-rustls", "hyper-util", "ipnet", "js-sys", @@ -1485,11 +1932,17 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", + "quinn", + "rustls", + "rustls-native-certs", + "rustls-pemfile", + "rustls-pki-types", "serde", "serde_json", "serde_urlencoded", "sync_wrapper", "tokio", + "tokio-rustls", "tower", "tower-service", "url", @@ -1499,12 +1952,32 @@ dependencies = [ "windows-registry", ] +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.15", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + [[package]] name = "rustc-demangle" version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + [[package]] name = "rustix" version = "0.38.44" @@ -1518,6 +1991,62 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "rustls" +version = "0.23.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c665f33d38cea657d9614f766881e4d510e0eda4239891eea56b4cadcf01801b" +dependencies = [ + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-native-certs" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63" +dependencies = [ + "openssl-probe", + "rustls-pki-types", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-pemfile" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd" +dependencies = [ + "web-time", + "zeroize", +] + +[[package]] +name = "rustls-webpki" +version = "0.103.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7df23109aa6c1567d1c575b9952556388da57401e4ace1d15f79eedad0d8f53" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + [[package]] name = "rustversion" version = "1.0.20" @@ -1531,23 +2060,71 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] -name = "serde" -version = "1.0.218" +name = "schannel" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8dfc9d19bdbf6d17e22319da49161d5d0108e4188e8b680aef6299eed22df60" +checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "security-framework" +version = "3.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d17b898a6d6948c3a8ee4372c17cb384f90d2e6e912ef00895b14fd7ab54ec38" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "321c8673b092a9a42605034a9879d73cb79101ed5fd117bc9a597b89b4e9e61a" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "semver" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.218" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f09503e191f4e797cb8aac08e9a4a4695c5edf6a2e70e376d961ddd5c969f82b" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.117", ] [[package]] @@ -1580,16 +2157,16 @@ checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.117", ] [[package]] name = "serde_spanned" -version = "0.6.8" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +checksum = "f8bbf91e5a4d6315eee45e704372590b30e260ee83af6639d64557f51b067776" dependencies = [ - "serde", + "serde_core", ] [[package]] @@ -1614,13 +2191,46 @@ dependencies = [ "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.7.1", + "indexmap 2.13.0", "serde", "serde_derive", "serde_json", "time", ] +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.7", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.7", +] + +[[package]] +name = "sha2" +version = "0.11.0-rc.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c5f3b1e2dc8aad28310d8410bd4d7e180eca65fca176c52ab00d364475d0024" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.11.1", +] + [[package]] name = "sharded-slab" version = "0.1.7" @@ -1651,6 +2261,12 @@ dependencies = [ "libc", ] +[[package]] +name = "simd-adler32" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" + [[package]] name = "slab" version = "0.4.9" @@ -1706,9 +2322,15 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.117", ] +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + [[package]] name = "supports-color" version = "2.1.0" @@ -1753,9 +2375,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.99" +version = "2.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e02e925281e18ffd9d640e234264753c43edc62d64b2d4cf898f1bc5e75f3fc2" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" dependencies = [ "proc-macro2", "quote", @@ -1779,7 +2401,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.117", ] [[package]] @@ -1851,7 +2473,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.117", ] [[package]] @@ -1862,7 +2484,7 @@ checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.117", ] [[package]] @@ -1877,30 +2499,31 @@ dependencies = [ [[package]] name = "time" -version = "0.3.37" +version = "0.3.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21" +checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" dependencies = [ "deranged", "itoa", + "js-sys", "num-conv", "powerfmt", - "serde", + "serde_core", "time-core", "time-macros", ] [[package]] name = "time-core" -version = "0.1.2" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" +checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" [[package]] name = "time-macros" -version = "0.2.19" +version = "0.2.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de" +checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" dependencies = [ "num-conv", "time-core", @@ -1916,6 +2539,21 @@ dependencies = [ "zerovec", ] +[[package]] +name = "tinyvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + [[package]] name = "tokio" version = "1.43.0" @@ -1941,7 +2579,17 @@ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.117", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" +dependencies = [ + "rustls", + "tokio", ] [[package]] @@ -1959,38 +2607,43 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.20" +version = "1.0.3+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd87a5cdd6ffab733b2f74bc4fd7ee5fff6634124999ac278c35fc78c6120148" +checksum = "c7614eaf19ad818347db24addfa201729cf2a9b6fdfd9eb0ab870fcacc606c0c" dependencies = [ - "serde", + "indexmap 2.13.0", + "serde_core", "serde_spanned", "toml_datetime", - "toml_edit", + "toml_parser", + "toml_writer", + "winnow", ] [[package]] name = "toml_datetime" -version = "0.6.8" +version = "1.0.0+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +checksum = "32c2555c699578a4f59f0cc68e5116c8d7cabbd45e1409b989d4be085b53f13e" dependencies = [ - "serde", + "serde_core", ] [[package]] -name = "toml_edit" -version = "0.22.24" +name = "toml_parser" +version = "1.0.9+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" +checksum = "702d4415e08923e7e1ef96cd5727c0dfed80b4d2fa25db9647fe5eb6f7c5a4c4" dependencies = [ - "indexmap 2.7.1", - "serde", - "serde_spanned", - "toml_datetime", "winnow", ] +[[package]] +name = "toml_writer" +version = "1.0.6+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab16f14aed21ee8bfd8ec22513f7287cd4a91aa92e44edfe2c17ddd004e92607" + [[package]] name = "tower" version = "0.5.2" @@ -2039,7 +2692,7 @@ checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.117", ] [[package]] @@ -2083,6 +2736,18 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" +[[package]] +name = "typed-path" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e28f89b80c87b8fb0cf04ab448d5dd0dd0ade2f8891bae878de66a75a28600e" + +[[package]] +name = "typenum" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" + [[package]] name = "unicode-ident" version = "1.0.18" @@ -2107,6 +2772,24 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "unit-prefix" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81e544489bf3d8ef66c953931f56617f423cd4b5494be343d9b9d3dda037b9a3" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + [[package]] name = "url" version = "2.5.4" @@ -2148,6 +2831,12 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + [[package]] name = "want" version = "0.3.1" @@ -2163,6 +2852,24 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasip2" +version = "1.0.2+wasi-0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +dependencies = [ + "wit-bindgen", +] + [[package]] name = "wasm-bindgen" version = "0.2.100" @@ -2185,7 +2892,7 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.117", "wasm-bindgen-shared", ] @@ -2220,7 +2927,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.117", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -2234,6 +2941,40 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "wasm-encoder" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +dependencies = [ + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +dependencies = [ + "anyhow", + "indexmap 2.13.0", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasmparser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags", + "hashbrown 0.15.2", + "indexmap 2.13.0", + "semver", +] + [[package]] name = "web-sys" version = "0.3.77" @@ -2244,6 +2985,16 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "winapi" version = "0.3.9" @@ -2281,6 +3032,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6dccfd733ce2b1753b03b6d3c65edf020262ea35e20ccdf3e288043e6dd620e3" +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + [[package]] name = "windows-registry" version = "0.2.0" @@ -2329,6 +3086,15 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link 0.2.1", +] + [[package]] name = "windows-targets" version = "0.52.6" @@ -2395,11 +3161,96 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.7.3" +version = "0.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e7f4ea97f6f78012141bcdb6a216b2609f0979ada50b20ca5b52dde2eac2bb1" +checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" + +[[package]] +name = "wit-bindgen" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" dependencies = [ - "memchr", + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen-core" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +dependencies = [ + "anyhow", + "heck", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" +dependencies = [ + "anyhow", + "heck", + "indexmap 2.13.0", + "prettyplease", + "syn 2.0.117", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn 2.0.117", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" +dependencies = [ + "anyhow", + "bitflags", + "indexmap 2.13.0", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +dependencies = [ + "anyhow", + "id-arena", + "indexmap 2.13.0", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", ] [[package]] @@ -2445,7 +3296,7 @@ checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.117", "synstructure", ] @@ -2467,7 +3318,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.117", ] [[package]] @@ -2487,10 +3338,30 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.117", "synstructure", ] +[[package]] +name = "zeroize" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "zerovec" version = "0.10.4" @@ -2510,5 +3381,78 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.99", + "syn 2.0.117", +] + +[[package]] +name = "zip" +version = "8.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e499faf5c6b97a0d086f4a8733de6d47aee2252b8127962439d8d4311a73f72" +dependencies = [ + "aes", + "bzip2", + "constant_time_eq", + "crc32fast", + "deflate64", + "flate2", + "getrandom 0.4.1", + "hmac", + "indexmap 2.13.0", + "lzma-rust2", + "memchr", + "pbkdf2", + "ppmd-rust", + "sha1", + "time", + "typed-path", + "zeroize", + "zopfli", + "zstd", +] + +[[package]] +name = "zlib-rs" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b20717f0917c908dc63de2e44e97f1e6b126ca58d0e391cee86d504eb8fbd05" + +[[package]] +name = "zopfli" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f05cd8797d63865425ff89b5c4a48804f35ba0ce8d125800027ad6017d2b5249" +dependencies = [ + "bumpalo", + "crc32fast", + "log", + "simd-adler32", +] + +[[package]] +name = "zstd" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "7.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d" +dependencies = [ + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.16+zstd.1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e19ebc2adc8f83e43039e79776e3fda8ca919132d68a1fed6a5faca2683748" +dependencies = [ + "cc", + "pkg-config", ] diff --git a/Cargo.toml b/Cargo.toml index 9ac03ff..c4e25f5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,12 +15,12 @@ tokio = { version = "1.38.0", features = ["fs", "macros", "signal", "rt-multi-th futures-util = "0.3.30" termion = "4.0.1" opener = "0.7.1" -toml = "0.8.14" +toml = "1.0.3" directories-next = "2.0.0" serde = "1.0.203" serde_json = "1.0.117" petname = "2.0.2" -reqwest = { version = "0.12.4", default-features = false } +reqwest = { version = "0.12.4", default-features = false, features = ["rustls-tls-native-roots"] } tar = "0.4.41" flate2 = "1.0.30" async-trait = "0.1.80" @@ -35,6 +35,10 @@ itertools = { version = "0.14.0", features = ["use_alloc"] } local-ip-address = "0.6.5" strum = { version = "0.27.2", features = ["derive"] } owo-colors = { version = "4.2.2", features = ["supports-colors"] } +zip = "8.1.0" +sha2 = "0.11.0-rc.5" +base16ct = { version = "1.0.0", features = ["alloc"] } +indicatif = "0.18.4" hyper-reverse-proxy = { version = "0.5.2-dev", git = "https://github.com/chpio/hyper-reverse-proxy", rev = "6934877eb74465204f605cc1c05ca5a9772db7c0" } hyper = "1.6.0" diff --git a/README.md b/README.md index 82e8bd7..bda3823 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ See the [configuration section](#configuration) for more options. #### Start an instance ```bash -haze start [database] [php-version] [services] +haze start [database] [php-version] [services] [vX.Y.Z] ``` Where `database` is one of `sqlite`, `mysql`, `mariadb`, `pgsql` or `oracle` @@ -69,6 +69,9 @@ might be missing some newer features. Each php version also comes with a `-dbg` variant that has php compiled in debug mode and can be used for debugging php itself with gdb. +You can specify a version number (e.g. `v32.0.2`) to use the sources from a +release instead of using the local sources. + Additionally, you can use the following options when starting an instance: - `s3`: set up an S3 server and configure to Nextcloud to use it as primary diff --git a/src/cloud.rs b/src/cloud.rs index 70ee931..ac782bd 100644 --- a/src/cloud.rs +++ b/src/cloud.rs @@ -5,6 +5,7 @@ use crate::mapping::{default_mappings, Mapping}; use crate::php::{PhpVersion, PHP_MEMORY_LIMIT}; use crate::service::Service; use crate::service::ServiceTrait; +use crate::sources::download_nc; use bollard::container::{ListContainersOptions, RemoveContainerOptions, UpdateContainerOptions}; use bollard::models::ContainerState; use bollard::network::CreateNetworkOptions; @@ -57,6 +58,7 @@ pub struct CloudOptions { pub php: PhpVersion, pub services: Vec, pub app_packages: Vec, + pub version: Option, } impl CloudOptions { @@ -69,6 +71,7 @@ impl CloudOptions { db: Database::default(), services: vec![], app_packages: vec![], + version: None, } } @@ -82,6 +85,7 @@ impl CloudOptions { let mut name = None; let mut services = Vec::new(); let mut app_package = Vec::new(); + let mut version = None; while let Some(option) = args.peek() { if let Ok(db_option) = Database::from_str(option.as_ref()) { @@ -96,6 +100,9 @@ impl CloudOptions { } else if option.as_ref().ends_with(".tar.gz") { app_package.push(option.to_string().into()); let _ = args.next(); + } else if let Some(v) = option.as_ref().strip_prefix("v") { + version = Some(v.into()); + let _ = args.next(); } else if option.as_ref() == "--name" { let _ = args.next(); name = args.next().map(|s| s.into()); @@ -112,6 +119,7 @@ impl CloudOptions { .unwrap_or_default(), services, app_packages: app_package, + version, }) } } @@ -245,6 +253,12 @@ impl Cloud { .wrap_err("Failed to create directory for app packages")?; } + let source_root = if let Some(version) = options.version.as_deref() { + download_nc(config, version).await? + } else { + config.sources_root.clone() + }; + let app_volumes = options .app_packages .iter() @@ -327,7 +341,7 @@ impl Cloud { ]; let volumes: Vec = mappings .into_iter() - .filter_map(|mapping| mapping.get_volume_arg(&id, config)) + .filter_map(|mapping| mapping.get_volume_arg(&id, config, &source_root)) .collect(); if let Some(db_name) = options @@ -382,6 +396,7 @@ impl Cloud { gateway, &options.services, &config.proxy, + options.version.as_deref(), ) .await .wrap_err("Failed to start php container") @@ -620,6 +635,7 @@ impl Cloud { let labels = cloud.labels?; let db = labels.get("haze-db")?.parse().ok()?; let php = labels.get("haze-php")?.parse().ok()?; + let version = labels.get("haze-version").cloned(); let found_services = labels .get("haze-services")? @@ -665,6 +681,7 @@ impl Cloud { db, services: found_services, app_packages: vec![], + version, }, pinned, address, @@ -788,7 +805,11 @@ impl Cloud { for mapping in mappings { if let Some(rel_path) = path.strip_prefix(mapping.target.as_str()) { let rel_path = rel_path.trim_matches('/'); - return Some(mapping.source(&self.id, config).join(rel_path)); + return Some( + mapping + .source(&self.id, config, &config.sources_root) + .join(rel_path), + ); } } None diff --git a/src/help.rs b/src/help.rs index f868bd6..97af8c8 100644 --- a/src/help.rs +++ b/src/help.rs @@ -88,6 +88,7 @@ fn subcommand_help(command: &dyn SubCommand) { print!(" {}", "[php version]".green()); print!(" {}", "[database type]".green()); print!(" {}", "[services]".green()); + print!(" {}", "[vX.Y.Z]".green()); } let args = if let Some(args) = command.get_str("Args") { diff --git a/src/main.rs b/src/main.rs index 485867c..5deb172 100644 --- a/src/main.rs +++ b/src/main.rs @@ -103,13 +103,18 @@ async fn main() -> Result { services.push(cloud.db().name()); let services = services.join(", "); let pin = if cloud.pinned { "*" } else { "" }; + let version = match cloud.options.version.as_ref() { + Some(version) => format!(", v{version}"), + None => String::new(), + }; println!( - "Cloud {}{}, {}, {}, running on {}", + "Cloud {}{}, {}, {}{}, running on {}", cloud.id, pin, cloud.php().name(), services, - cloud.address + version, + cloud.address, ); } } diff --git a/src/mapping.rs b/src/mapping.rs index b224f62..4061100 100644 --- a/src/mapping.rs +++ b/src/mapping.rs @@ -78,20 +78,25 @@ impl<'a> Mapping<'a> { Ok(()) } - pub fn source(&self, id: &str, config: &HazeConfig) -> Utf8PathBuf { + pub fn source(&self, id: &str, config: &HazeConfig, source_root: &Utf8Path) -> Utf8PathBuf { match self.source_type { MappingSourceType::WorkDir => config.work_dir.join(id).join(self.source), MappingSourceType::GlobalWorkDir => config.work_dir.join(self.source), - MappingSourceType::Sources => config.sources_root.join(self.source), + MappingSourceType::Sources => source_root.join(self.source), MappingSourceType::Absolute => self.source.into(), } } - pub fn get_volume_arg(&self, id: &str, config: &HazeConfig) -> Option { + pub fn get_volume_arg( + &self, + id: &str, + config: &HazeConfig, + source_root: &Utf8Path, + ) -> Option { if !self.map { return None; } - let source = self.source(id, config); + let source = self.source(id, config, source_root); Some(if self.read_only { format!("{}:{}:ro", source, self.target) } else { diff --git a/src/php.rs b/src/php.rs index 222d5cb..9eabcaa 100644 --- a/src/php.rs +++ b/src/php.rs @@ -164,6 +164,7 @@ impl PhpVersion { host: &str, services: &[Service], proxy_config: &ProxyConfig, + version: Option<&str>, ) -> Result { ensure_network_exists(docker, "haze").await?; pull_image(docker, self.image()).await?; @@ -192,6 +193,17 @@ impl PhpVersion { proxy_config.addr(id, IpAddr::V4(Ipv4Addr::LOCALHOST)) )); + let mut labels = hashmap! { + "haze-type".to_string() => "cloud".to_string(), + "haze-db".to_string() => db.name().to_string(), + "haze-php".to_string() => self.name().to_string(), + "haze-cloud-id".to_string() => id.to_string(), + "haze-services".to_string() => services.iter().map(|s| s.name()).join(","), + }; + if let Some(version) = version { + labels.insert("haze-version".to_string(), version.to_string()); + } + let config = Config { image: Some(self.image().to_string()), env: Some(env), @@ -211,13 +223,7 @@ impl PhpVersion { } }, }), - labels: Some(hashmap! { - "haze-type".to_string() => "cloud".to_string(), - "haze-db".to_string() => db.name().to_string(), - "haze-php".to_string() => self.name().to_string(), - "haze-cloud-id".to_string() => id.to_string(), - "haze-services".to_string() => services.iter().map(|s| s.name()).join(","), - }), + labels: Some(labels), ..Default::default() }; diff --git a/src/sources.rs b/src/sources.rs index 0a19e08..b99c54a 100644 --- a/src/sources.rs +++ b/src/sources.rs @@ -1,6 +1,17 @@ +use crate::config::HazeConfig; +use camino::Utf8PathBuf; +use indicatif::{MultiProgress, ProgressBar, ProgressStyle}; use miette::{Context, IntoDiagnostic, Report, Result}; +use reqwest::header::HeaderName; +use reqwest::{Client, IntoUrl, Response}; +use sha2::{Digest, Sha512}; use std::fs::read_to_string; +use std::io::Cursor; use std::path::{Path, PathBuf}; +use std::time::Duration; +use tokio::fs::create_dir_all; +use zip::read::root_dir_common_filter; +use zip::ZipArchive; pub struct Sources { #[allow(dead_code)] @@ -49,3 +60,116 @@ impl Sources { } } } + +pub async fn download_nc(config: &HazeConfig, version: &str) -> Result { + if !version.chars().all(|c| c.is_ascii_digit() || c == '.') { + return Err(Report::msg(format!("Invalid version: {version}"))); + } + let root = config.work_dir.join("sources"); + create_dir_all(&root) + .await + .into_diagnostic() + .wrap_err("failed to create parent directory for sources")?; + let dest = root.join(version); + if !dest.exists() { + let progress = MultiProgress::new(); + let download_style = ProgressStyle::with_template("{spinner:.green} {msg} [{elapsed_precise}] [{bar:.cyan/blue}] {bytes}/{total_bytes} ({bytes_per_sec}, {eta})") + .unwrap(); + let download_bar = ProgressBar::new(0) + .with_message("Downloading") + .with_style(download_style.clone()); + let download_bar = progress.add(download_bar); + + let archive = download_url( + format!("https://download.nextcloud.com/server/releases/nextcloud-{version}.zip"), + |size| { + download_bar.set_length(size); + }, + |count| { + download_bar.inc(count); + }, + ) + .await + .wrap_err_with(|| format!("Failed to download archive for {}", version))?; + download_bar.finish(); + + let expected_hash = download_text(format!( + "https://download.nextcloud.com/server/releases/nextcloud-{version}.zip.sha512" + )) + .await + .wrap_err_with(|| format!("Failed to download hash for {}", version))?; + let expected_hash = expected_hash + .split_once(' ') + .map(|(hash, _)| hash) + .unwrap_or(expected_hash.trim()); + + let hash_bar = ProgressBar::new(download_bar.length().unwrap_or_default()) + .with_message("Validating") + .with_style(download_style.clone()); + let hash_bar = progress.add(hash_bar); + let mut hasher = Sha512::new(); + for chunk in archive.chunks(1014 * 1024) { + hash_bar.inc(chunk.len() as u64); + hasher.update(chunk); + } + let hash = hasher.finalize(); + + let hash = base16ct::lower::encode_string(&hash); + if expected_hash != hash { + return Err(Report::msg(format!( + "Invalid hash for downloaded: {version}, expected {expected_hash} but got {hash}" + ))); + } + hash_bar.finish(); + + let extract_bar = ProgressBar::new_spinner().with_message("Extracing"); + extract_bar.enable_steady_tick(Duration::from_millis(100)); + let extract_bar = progress.add(extract_bar); + let mut archive = ZipArchive::new(Cursor::new(archive)).into_diagnostic()?; + archive + .extract_unwrapped_root_dir(&dest, root_dir_common_filter) + .into_diagnostic() + .wrap_err("Failed to extract archive")?; + extract_bar.finish(); + } + Ok(dest) +} + +async fn download_url( + url: U, + size: SizeFN, + progress: ProgressFN, +) -> Result> { + let mut res = get_url(url).await?.error_for_status().into_diagnostic()?; + let mut buff = Vec::new(); + + size(res.content_length().unwrap_or_default()); + while let Some(chunk) = res.chunk().await.into_diagnostic()? { + progress(chunk.len() as u64); + buff.extend(chunk); + } + Ok(buff) +} +async fn download_text(url: U) -> Result { + get_url(url) + .await? + .error_for_status() + .into_diagnostic()? + .text() + .await + .into_diagnostic() +} + +async fn get_url(url: U) -> Result { + Client::builder() + .build() + .into_diagnostic()? + .get(url) + .header( + HeaderName::from_static("user-agent"), + format!("haze {}", env!("CARGO_PKG_VERSION")), + ) + .send() + .await + .into_diagnostic() +} From df38f16f10a53ef2058c49fa71bf8fa1bcae7006 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Fri, 27 Feb 2026 22:52:40 +0100 Subject: [PATCH 07/68] update bollard --- Cargo.lock | 286 ++++++++++++++++--------------------- Cargo.toml | 2 +- src/cloud.rs | 22 +-- src/database.rs | 32 ++--- src/exec.rs | 4 +- src/image.rs | 54 ++++--- src/network.rs | 11 +- src/php.rs | 26 ++-- src/service.rs | 2 +- src/service/clam.rs | 67 ++++----- src/service/dav.rs | 28 ++-- src/service/imaginary.rs | 26 ++-- src/service/kaspersky.rs | 46 +++--- src/service/ldap.rs | 55 ++++--- src/service/mail.rs | 25 ++-- src/service/objectstore.rs | 31 ++-- src/service/oc.rs | 28 ++-- src/service/office.rs | 29 ++-- src/service/onlyoffice.rs | 27 ++-- src/service/push.rs | 28 ++-- src/service/redis.rs | 25 ++-- src/service/sftp.rs | 27 ++-- src/service/smb.rs | 31 ++-- 23 files changed, 410 insertions(+), 502 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ce6684d..74b1270 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -28,21 +28,6 @@ dependencies = [ "cpufeatures", ] -[[package]] -name = "android-tzdata" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" - -[[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] - [[package]] name = "anstream" version = "0.6.18" @@ -199,7 +184,7 @@ dependencies = [ "miniz_oxide", "object", "rustc-demangle", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -249,9 +234,9 @@ dependencies = [ [[package]] name = "bollard" -version = "0.18.1" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97ccca1260af6a459d75994ad5acc1651bcabcbdbc41467cc9786519ab854c30" +checksum = "227aa051deec8d16bd9c34605e7aaf153f240e35483dd42f6f78903847934738" dependencies = [ "base64", "bollard-stubs", @@ -270,7 +255,6 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "serde_repr", "serde_urlencoded", "thiserror 2.0.12", "tokio", @@ -282,13 +266,13 @@ dependencies = [ [[package]] name = "bollard-stubs" -version = "1.47.1-rc.27.3.1" +version = "1.52.1-rc.29.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f179cfbddb6e77a5472703d4b30436bff32929c0aa8a9008ecf23d1d3cdd0da" +checksum = "0f0a8ca8799131c1837d1282c3f81f31e76ceb0ce426e04a7fe1ccee3287c066" dependencies = [ "serde", + "serde_json", "serde_repr", - "serde_with", ] [[package]] @@ -361,19 +345,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" -[[package]] -name = "chrono" -version = "0.4.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a7964611d71df112cb1730f2ee67324fcf4d0fc6606acbbe9bfe06df124637c" -dependencies = [ - "android-tzdata", - "iana-time-zone", - "num-traits", - "serde", - "windows-link 0.1.0", -] - [[package]] name = "cipher" version = "0.4.4" @@ -532,7 +503,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" dependencies = [ "powerfmt", - "serde_core", ] [[package]] @@ -808,19 +778,13 @@ dependencies = [ "futures-core", "futures-sink", "http", - "indexmap 2.13.0", + "indexmap", "slab", "tokio", "tokio-util", "tracing", ] -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" - [[package]] name = "hashbrown" version = "0.15.2" @@ -1045,7 +1009,7 @@ dependencies = [ "http-body", "hyper", "pin-project-lite", - "socket2", + "socket2 0.5.8", "tokio", "tower-service", "tracing", @@ -1066,29 +1030,6 @@ dependencies = [ "tower-service", ] -[[package]] -name = "iana-time-zone" -version = "0.1.61" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "wasm-bindgen", - "windows-core", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" -dependencies = [ - "cc", -] - [[package]] name = "icu_collections" version = "1.5.0" @@ -1234,17 +1175,6 @@ dependencies = [ "icu_properties", ] -[[package]] -name = "indexmap" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" -dependencies = [ - "autocfg", - "hashbrown 0.12.3", - "serde", -] - [[package]] name = "indexmap" version = "2.13.0" @@ -1362,9 +1292,9 @@ checksum = "2c4a545a15244c7d945065b5d392b2d2d7f21526fba56ce51467b06ed445e8f7" [[package]] name = "libc" -version = "0.2.170" +version = "0.2.182" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "875b3680cb2f8f71bdcf9a30f38d48282f5d3c95cbf9b3fa57269bb5d5c06828" +checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112" [[package]] name = "libdbus-sys" @@ -1590,15 +1520,6 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050" -[[package]] -name = "num-traits" -version = "0.2.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" -dependencies = [ - "autocfg", -] - [[package]] name = "numtoa" version = "0.2.4" @@ -1761,7 +1682,7 @@ dependencies = [ "quinn-udp", "rustc-hash", "rustls", - "socket2", + "socket2 0.5.8", "thiserror 2.0.12", "tokio", "tracing", @@ -1798,7 +1719,7 @@ dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2", + "socket2 0.5.8", "tracing", "windows-sys 0.59.0", ] @@ -2181,23 +2102,6 @@ dependencies = [ "serde", ] -[[package]] -name = "serde_with" -version = "3.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6b6f7f2fcb69f747921f79f3926bd1e203fce4fef62c268dd3abfb6d86029aa" -dependencies = [ - "base64", - "chrono", - "hex", - "indexmap 1.9.3", - "indexmap 2.13.0", - "serde", - "serde_derive", - "serde_json", - "time", -] - [[package]] name = "sha1" version = "0.10.6" @@ -2292,6 +2196,16 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "socket2" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86f4aa3ad99f2088c990dfa82d367e19cb29268ed67c574d10d0a4bfe71f07e0" +dependencies = [ + "libc", + "windows-sys 0.60.2", +] + [[package]] name = "stable_deref_trait" version = "1.2.0" @@ -2504,13 +2418,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" dependencies = [ "deranged", - "itoa", "js-sys", "num-conv", "powerfmt", "serde_core", "time-core", - "time-macros", ] [[package]] @@ -2519,16 +2431,6 @@ version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" -[[package]] -name = "time-macros" -version = "0.2.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" -dependencies = [ - "num-conv", - "time-core", -] - [[package]] name = "tinystr" version = "0.7.6" @@ -2556,26 +2458,25 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.43.0" +version = "1.49.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d61fa4ffa3de412bfea335c6ecff681de2b609ba3c77ef3e00e521813a9ed9e" +checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86" dependencies = [ - "backtrace", "bytes", "libc", "mio", "pin-project-lite", "signal-hook-registry", - "socket2", + "socket2 0.6.2", "tokio-macros", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] name = "tokio-macros" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" +checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" dependencies = [ "proc-macro2", "quote", @@ -2611,7 +2512,7 @@ version = "1.0.3+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7614eaf19ad818347db24addfa201729cf2a9b6fdfd9eb0ab870fcacc606c0c" dependencies = [ - "indexmap 2.13.0", + "indexmap", "serde_core", "serde_spanned", "toml_datetime", @@ -2958,7 +2859,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" dependencies = [ "anyhow", - "indexmap 2.13.0", + "indexmap", "wasm-encoder", "wasmparser", ] @@ -2971,7 +2872,7 @@ checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" dependencies = [ "bitflags", "hashbrown 0.15.2", - "indexmap 2.13.0", + "indexmap", "semver", ] @@ -3017,21 +2918,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows-core" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" -dependencies = [ - "windows-targets", -] - -[[package]] -name = "windows-link" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dccfd733ce2b1753b03b6d3c65edf020262ea35e20ccdf3e288043e6dd620e3" - [[package]] name = "windows-link" version = "0.2.1" @@ -3046,7 +2932,7 @@ checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" dependencies = [ "windows-result", "windows-strings", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -3055,7 +2941,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -3065,7 +2951,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" dependencies = [ "windows-result", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -3074,7 +2960,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -3083,7 +2969,16 @@ version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.5", ] [[package]] @@ -3092,7 +2987,7 @@ version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" dependencies = [ - "windows-link 0.2.1", + "windows-link", ] [[package]] @@ -3101,14 +2996,31 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", ] [[package]] @@ -3117,48 +3029,96 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" +[[package]] +name = "windows_i686_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_i686_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" + [[package]] name = "winnow" version = "0.7.14" @@ -3193,7 +3153,7 @@ checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" dependencies = [ "anyhow", "heck", - "indexmap 2.13.0", + "indexmap", "prettyplease", "syn 2.0.117", "wasm-metadata", @@ -3224,7 +3184,7 @@ checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" dependencies = [ "anyhow", "bitflags", - "indexmap 2.13.0", + "indexmap", "log", "serde", "serde_derive", @@ -3243,7 +3203,7 @@ checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" dependencies = [ "anyhow", "id-arena", - "indexmap 2.13.0", + "indexmap", "log", "semver", "serde", @@ -3398,7 +3358,7 @@ dependencies = [ "flate2", "getrandom 0.4.1", "hmac", - "indexmap 2.13.0", + "indexmap", "lzma-rust2", "memchr", "pbkdf2", diff --git a/Cargo.toml b/Cargo.toml index c4e25f5..c796a67 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT" description = "Easy setup and management of Nextcloud test instances using docker" [dependencies] -bollard = "0.18.1" +bollard = "0.20.1" maplit = "1.0.2" camino = { version = "1.1.7", features = ["serde1"] } tokio = { version = "1.38.0", features = ["fs", "macros", "signal", "rt-multi-thread"] } diff --git a/src/cloud.rs b/src/cloud.rs index ac782bd..79d298c 100644 --- a/src/cloud.rs +++ b/src/cloud.rs @@ -6,9 +6,9 @@ use crate::php::{PhpVersion, PHP_MEMORY_LIMIT}; use crate::service::Service; use crate::service::ServiceTrait; use crate::sources::download_nc; -use bollard::container::{ListContainersOptions, RemoveContainerOptions, UpdateContainerOptions}; -use bollard::models::ContainerState; -use bollard::network::CreateNetworkOptions; +use bollard::config::NetworkCreateRequest; +use bollard::models::{ContainerState, ContainerUpdateBody}; +use bollard::query_parameters::{ListContainersOptions, RemoveContainerOptions}; use bollard::Docker; use camino::{Utf8Path, Utf8PathBuf}; use flate2::read::GzDecoder; @@ -301,8 +301,8 @@ impl Cloud { } let network = docker - .create_network(CreateNetworkOptions { - name: id.as_str(), + .create_network(NetworkCreateRequest { + name: id.clone(), ..Default::default() }) .await @@ -310,7 +310,7 @@ impl Cloud { .id; let network_info = docker - .inspect_network::(&network, None) + .inspect_network(&network, None) .await .into_diagnostic()?; let gateway = network_info @@ -598,7 +598,7 @@ impl Cloud { config: &HazeConfig, ) -> Result> { let containers = docker - .list_containers::(Some(ListContainersOptions { + .list_containers(Some(ListContainersOptions { all: true, ..Default::default() })) @@ -752,9 +752,9 @@ impl Cloud { docker .update_container( &self.id, - UpdateContainerOptions:: { + ContainerUpdateBody { memory: Some(PHP_MEMORY_LIMIT + 1), - ..UpdateContainerOptions::default() + ..ContainerUpdateBody::default() }, ) .await @@ -766,9 +766,9 @@ impl Cloud { docker .update_container( &self.id, - UpdateContainerOptions:: { + ContainerUpdateBody { memory: Some(PHP_MEMORY_LIMIT), - ..UpdateContainerOptions::default() + ..ContainerUpdateBody::default() }, ) .await diff --git a/src/database.rs b/src/database.rs index d621e84..ff969a3 100644 --- a/src/database.rs +++ b/src/database.rs @@ -1,7 +1,8 @@ use crate::exec::{exec, exec_tty, ExitCode}; use crate::image::pull_image; -use bollard::container::{Config, CreateContainerOptions, NetworkingConfig}; -use bollard::models::{EndpointSettings, HostConfig}; +use bollard::config::ContainerCreateBody; +use bollard::models::{EndpointSettings, HostConfig, NetworkingConfig}; +use bollard::query_parameters::CreateContainerOptions; use bollard::Docker; use maplit::hashmap; use miette::{IntoDiagnostic, Report, Result, WrapErr}; @@ -193,35 +194,35 @@ impl Database { .wrap_err("Failed to pull database image")?; } let options = Some(CreateContainerOptions { - name: format!("{}-db{}", cloud_id, postfix), + name: Some(format!("{}-db{}", cloud_id, postfix)), ..CreateContainerOptions::default() }); - let config = Config { - image: Some(self.image()), - env: Some(self.env()), + let config = ContainerCreateBody { + image: Some(self.image().into()), + env: Some(self.env().into_iter().map(String::from).collect()), host_config: Some(HostConfig { network_mode: Some(network.to_string()), ..Default::default() }), labels: Some(hashmap! { - "haze-type" => "db", - "haze-cloud-id" => cloud_id + "haze-type".into() => "db".into(), + "haze-cloud-id".into() => cloud_id.into() }), networking_config: Some(NetworkingConfig { - endpoints_config: hashmap! { - network => EndpointSettings { + endpoints_config: Some(hashmap! { + String::from(network) => EndpointSettings { aliases: Some(vec![ format!("{}{}", self.name(), postfix), format!("db{}", postfix), ]), ..Default::default() } - }, + }), }), cmd: if self.image() == "mysql:8" { Some(vec![ - "--default-authentication-plugin", - "mysql_native_password", + "--default-authentication-plugin".into(), + "mysql_native_password".into(), ]) } else { None @@ -233,10 +234,7 @@ impl Database { .await .into_diagnostic()? .id; - docker - .start_container::(&id, None) - .await - .into_diagnostic()?; + docker.start_container(&id, None).await.into_diagnostic()?; Ok(Some(id)) } diff --git a/src/exec.rs b/src/exec.rs index ec67333..8d07508 100644 --- a/src/exec.rs +++ b/src/exec.rs @@ -1,5 +1,5 @@ -use bollard::container::LogsOptions; use bollard::exec::{CreateExecOptions, ResizeExecOptions, StartExecResults}; +use bollard::query_parameters::LogsOptions; use bollard::Docker; use futures_util::StreamExt; use miette::{IntoDiagnostic, Report, Result, WrapErr}; @@ -189,7 +189,7 @@ pub async fn container_logs( count: usize, follow: bool, ) -> Result<()> { - let mut stream = docker.logs::( + let mut stream = docker.logs( container, Some(LogsOptions { stdout: true, diff --git a/src/image.rs b/src/image.rs index 2ea9b7d..94da352 100644 --- a/src/image.rs +++ b/src/image.rs @@ -1,12 +1,10 @@ -use bollard::image::CreateImageOptions; use bollard::models::CreateImageInfo; +use bollard::query_parameters::CreateImageOptions; use bollard::Docker; use futures_util::StreamExt; +use indicatif::{MultiProgress, ProgressBar, ProgressStyle}; use miette::{IntoDiagnostic, Result, WrapErr}; use std::collections::HashMap; -use std::io::stdout; -use std::io::Write; -use termion::cursor; pub async fn image_exists(docker: &Docker, image: &str) -> bool { docker.inspect_image(image).await.is_ok() @@ -32,9 +30,9 @@ pub async fn force_pull_image(docker: &Docker, image: &str) -> Result<()> { let mut info_stream = docker.create_image( Some(CreateImageOptions { from_image: if image.contains(':') { - image.to_string() + Some(image.to_string()) } else { - format!("{}:latest", image) + Some(format!("{}:latest", image)) }, ..Default::default() }), @@ -42,35 +40,33 @@ pub async fn force_pull_image(docker: &Docker, image: &str) -> Result<()> { None, ); - let mut bars: HashMap = HashMap::new(); + let bar_style = ProgressStyle::with_template( + "{spinner:.green} {msg} [{elapsed_precise}] [{bar:.cyan/blue}] {bytes:>12}/{total_bytes}", + ) + .unwrap(); + let mut bars: HashMap = HashMap::new(); + let mp = MultiProgress::new(); - let mut stdout = stdout(); while let Some(info) = info_stream.next().await { let info: CreateImageInfo = info .into_diagnostic() .wrap_err_with(|| format!("Error while pulling image {}", image))?; - if let (Some(id), Some(status), Some(progress)) = (info.id, info.status, info.progress) { - match bars.get(&id) { - Some(pos) => { - let offset = bars.len() as u16 - pos; - write!( - stdout, - "{}{}{} - {:12} {}{}", - cursor::Save, - cursor::Up(offset), - id, - status, - progress, - cursor::Restore - ) - .into_diagnostic()?; - } - None => { - writeln!(stdout, "{} - {:12} {}", id, status, progress).into_diagnostic()?; - bars.insert(id, bars.len() as u16); - } + if let (Some(id), Some(status), Some(progress)) = + (info.id, info.status, info.progress_detail) + { + let bar = bars.entry(id.clone()).or_insert_with(|| { + let bar = ProgressBar::new(progress.total.unwrap_or_default() as u64) + .with_style(bar_style.clone()) + .with_message(format!("{id:20} - {status:10}")); + mp.add(bar) + }); + bar.set_message(format!("{id:10} - {status:20}")); + if let Some(total) = progress.total { + bar.set_length(total as u64); + } + if let Some(current) = progress.current { + bar.set_position(current as u64); } - stdout.flush().into_diagnostic()?; } } Ok(()) diff --git a/src/network.rs b/src/network.rs index ea589ec..0b25f2c 100644 --- a/src/network.rs +++ b/src/network.rs @@ -1,11 +1,11 @@ use crate::cloud::Cloud; -use bollard::network::CreateNetworkOptions; +use bollard::config::NetworkCreateRequest; use bollard::Docker; use miette::{IntoDiagnostic, Result, WrapErr}; pub async fn clear_networks(docker: &Docker, instances: &[Cloud]) -> Result<()> { let networks = docker - .list_networks::<&str>(None) + .list_networks(None) .await .into_diagnostic() .wrap_err("Failed to list docker networks")?; @@ -23,7 +23,7 @@ pub async fn clear_networks(docker: &Docker, instances: &[Cloud]) -> Result<()> async fn get_network_id(docker: &Docker, name: &str) -> Result> { let networks = docker - .list_networks::<&str>(None) + .list_networks(None) .await .into_diagnostic() .wrap_err("Failed to list docker networks")?; @@ -41,9 +41,8 @@ pub async fn ensure_network_exists(docker: &Docker, name: &str) -> Result EndpointSettings { aliases: Some(vec!["cloud".to_string()]), ..Default::default() } - }, + }), }), labels: Some(labels), ..Default::default() @@ -233,11 +233,7 @@ impl PhpVersion { .into_diagnostic()? .id; - if let Err(e) = docker - .start_container::(&id, None) - .await - .into_diagnostic() - { + if let Err(e) = docker.start_container(&id, None).await.into_diagnostic() { docker.remove_container(&id, None).await.ok(); return Err(e); } @@ -245,12 +241,12 @@ impl PhpVersion { if let Err(e) = docker .connect_network( "haze", - ConnectNetworkOptions { - container: id.as_str(), - endpoint_config: EndpointSettings { + NetworkConnectRequest { + container: id.to_string(), + endpoint_config: Some(EndpointSettings { aliases: Some(vec![id.to_string()]), ..Default::default() - }, + }), }, ) .await diff --git a/src/service.rs b/src/service.rs index 62b84d4..8874352 100644 --- a/src/service.rs +++ b/src/service.rs @@ -142,7 +142,7 @@ pub trait ServiceTrait { return Ok(Box::new(empty())); }; docker - .start_container::(&container, None) + .start_container(&container, None) .await .into_diagnostic()?; self.wait_for_running(docker, cloud_id).await?; diff --git a/src/service/clam.rs b/src/service/clam.rs index 924abd6..74b790d 100644 --- a/src/service/clam.rs +++ b/src/service/clam.rs @@ -4,8 +4,8 @@ use crate::exec::exec; use crate::image::pull_image; use crate::service::ServiceTrait; use crate::Result; -use bollard::container::{Config, CreateContainerOptions, NetworkingConfig}; -use bollard::models::{EndpointSettings, HostConfig}; +use bollard::models::{ContainerCreateBody, EndpointSettings, HostConfig, NetworkingConfig}; +use bollard::query_parameters::CreateContainerOptions; use bollard::Docker; use maplit::hashmap; use miette::{IntoDiagnostic, WrapErr}; @@ -40,26 +40,26 @@ impl ServiceTrait for ClamIcap { let image = "ghcr.io/icewind1991/icap-clamav-service-tls"; pull_image(docker, image).await?; let options = Some(CreateContainerOptions { - name: self.container_name(cloud_id).unwrap(), + name: self.container_name(cloud_id), ..CreateContainerOptions::default() }); - let config = Config { - image: Some(image), + let config = ContainerCreateBody { + image: Some(image.into()), host_config: Some(HostConfig { network_mode: Some(network.to_string()), ..Default::default() }), labels: Some(hashmap! { - "haze-type" => self.name(), - "haze-cloud-id" => cloud_id + "haze-type".into() => self.name().into(), + "haze-cloud-id".into() => cloud_id.into() }), networking_config: Some(NetworkingConfig { - endpoints_config: hashmap! { - network => EndpointSettings { + endpoints_config: Some(hashmap! { + network.into() => EndpointSettings { aliases: Some(vec![self.name().to_string()]), ..Default::default() } - }, + }), }), ..Default::default() }; @@ -68,10 +68,7 @@ impl ServiceTrait for ClamIcap { .await .into_diagnostic()? .id; - docker - .start_container::(&id, None) - .await - .into_diagnostic()?; + docker.start_container(&id, None).await.into_diagnostic()?; Ok(vec![id]) } @@ -129,26 +126,26 @@ impl ServiceTrait for ClamIcapTls { let image = "ghcr.io/icewind1991/icap-clamav-service-tls"; pull_image(docker, image).await?; let options = Some(CreateContainerOptions { - name: self.container_name(cloud_id).unwrap(), + name: self.container_name(cloud_id), ..CreateContainerOptions::default() }); - let config = Config { - image: Some(image), + let config = ContainerCreateBody { + image: Some(image.into()), host_config: Some(HostConfig { network_mode: Some(network.to_string()), ..Default::default() }), labels: Some(hashmap! { - "haze-type" => self.name(), - "haze-cloud-id" => cloud_id + "haze-type".into() => self.name().into(), + "haze-cloud-id".into() => cloud_id.into() }), networking_config: Some(NetworkingConfig { - endpoints_config: hashmap! { - network => EndpointSettings { + endpoints_config: Some(hashmap! { + network.into() => EndpointSettings { aliases: Some(vec![self.name().to_string()]), ..Default::default() } - }, + }), }), ..Default::default() }; @@ -157,10 +154,7 @@ impl ServiceTrait for ClamIcapTls { .await .into_diagnostic()? .id; - docker - .start_container::(&id, None) - .await - .into_diagnostic()?; + docker.start_container(&id, None).await.into_diagnostic()?; Ok(vec![id]) } @@ -255,26 +249,26 @@ impl ServiceTrait for ClamSocket { let image = "clamav/clamav"; pull_image(docker, image).await?; let options = Some(CreateContainerOptions { - name: self.container_name(cloud_id).unwrap(), + name: self.container_name(cloud_id), ..CreateContainerOptions::default() }); - let config = Config { - image: Some(image), + let config = ContainerCreateBody { + image: Some(image.into()), host_config: Some(HostConfig { network_mode: Some(network.to_string()), ..Default::default() }), labels: Some(hashmap! { - "haze-type" => self.name(), - "haze-cloud-id" => cloud_id + "haze-type".into() => self.name().into(), + "haze-cloud-id".into() => cloud_id.into() }), networking_config: Some(NetworkingConfig { - endpoints_config: hashmap! { - network => EndpointSettings { + endpoints_config: Some(hashmap! { + network.into() => EndpointSettings { aliases: Some(vec![self.name().to_string()]), ..Default::default() } - }, + }), }), ..Default::default() }; @@ -283,10 +277,7 @@ impl ServiceTrait for ClamSocket { .await .into_diagnostic()? .id; - docker - .start_container::(&id, None) - .await - .into_diagnostic()?; + docker.start_container(&id, None).await.into_diagnostic()?; Ok(vec![id]) } diff --git a/src/service/dav.rs b/src/service/dav.rs index bfd4904..d328925 100644 --- a/src/service/dav.rs +++ b/src/service/dav.rs @@ -3,8 +3,9 @@ use crate::config::HazeConfig; use crate::image::pull_image; use crate::service::ServiceTrait; use crate::Result; -use bollard::container::{Config, CreateContainerOptions, NetworkingConfig}; -use bollard::models::{EndpointSettings, HostConfig}; +use bollard::config::ContainerCreateBody; +use bollard::models::{EndpointSettings, HostConfig, NetworkingConfig}; +use bollard::query_parameters::CreateContainerOptions; use bollard::Docker; use maplit::hashmap; use miette::IntoDiagnostic; @@ -29,27 +30,27 @@ impl ServiceTrait for Dav { let image = "ugeek/webdav:amd64"; pull_image(docker, image).await?; let options = Some(CreateContainerOptions { - name: self.container_name(cloud_id).unwrap(), + name: self.container_name(cloud_id), ..CreateContainerOptions::default() }); - let config = Config { - image: Some(image), + let config = ContainerCreateBody { + image: Some(image.into()), host_config: Some(HostConfig { network_mode: Some(network.to_string()), ..Default::default() }), - env: Some(vec!["USERNAME=test", "PASSWORD=test"]), + env: Some(vec!["USERNAME=test".into(), "PASSWORD=test".into()]), labels: Some(hashmap! { - "haze-type" => self.name(), - "haze-cloud-id" => cloud_id + "haze-type".into() => self.name().into(), + "haze-cloud-id".into() => cloud_id.into() }), networking_config: Some(NetworkingConfig { - endpoints_config: hashmap! { - network => EndpointSettings { + endpoints_config: Some(hashmap! { + network.into() => EndpointSettings { aliases: Some(vec![self.name().to_string()]), ..Default::default() } - }, + }), }), ..Default::default() }; @@ -58,10 +59,7 @@ impl ServiceTrait for Dav { .await .into_diagnostic()? .id; - docker - .start_container::(&id, None) - .await - .into_diagnostic()?; + docker.start_container(&id, None).await.into_diagnostic()?; Ok(vec![id]) } diff --git a/src/service/imaginary.rs b/src/service/imaginary.rs index 986b242..693c173 100644 --- a/src/service/imaginary.rs +++ b/src/service/imaginary.rs @@ -3,8 +3,9 @@ use crate::config::HazeConfig; use crate::image::pull_image; use crate::service::ServiceTrait; use crate::Result; -use bollard::container::{Config, CreateContainerOptions, NetworkingConfig}; -use bollard::models::{EndpointSettings, HostConfig}; +use bollard::config::NetworkingConfig; +use bollard::models::{ContainerCreateBody, EndpointSettings, HostConfig}; +use bollard::query_parameters::CreateContainerOptions; use bollard::Docker; use maplit::hashmap; use miette::IntoDiagnostic; @@ -29,26 +30,26 @@ impl ServiceTrait for Imaginary { let image = "nextcloud/aio-imaginary:latest"; pull_image(docker, image).await?; let options = Some(CreateContainerOptions { - name: self.container_name(cloud_id).unwrap(), + name: self.container_name(cloud_id), ..CreateContainerOptions::default() }); - let config = Config { - image: Some(image), + let config = ContainerCreateBody { + image: Some(image.into()), host_config: Some(HostConfig { network_mode: Some(network.to_string()), ..Default::default() }), labels: Some(hashmap! { - "haze-type" => self.name(), - "haze-cloud-id" => cloud_id + "haze-type".into() => self.name().into(), + "haze-cloud-id".into() => cloud_id.into() }), networking_config: Some(NetworkingConfig { - endpoints_config: hashmap! { - network => EndpointSettings { + endpoints_config: Some(hashmap! { + network.into() => EndpointSettings { aliases: Some(vec![self.name().to_string()]), ..Default::default() } - }, + }), }), ..Default::default() }; @@ -57,10 +58,7 @@ impl ServiceTrait for Imaginary { .await .into_diagnostic()? .id; - docker - .start_container::(&id, None) - .await - .into_diagnostic()?; + docker.start_container(&id, None).await.into_diagnostic()?; Ok(vec![id]) } diff --git a/src/service/kaspersky.rs b/src/service/kaspersky.rs index 8199429..bc4f08a 100644 --- a/src/service/kaspersky.rs +++ b/src/service/kaspersky.rs @@ -4,8 +4,8 @@ use crate::exec::exec; use crate::image::{image_exists, pull_image}; use crate::service::ServiceTrait; use crate::Result; -use bollard::container::{Config, CreateContainerOptions, NetworkingConfig}; -use bollard::models::{EndpointSettings, HostConfig}; +use bollard::models::{ContainerCreateBody, EndpointSettings, HostConfig, NetworkingConfig}; +use bollard::query_parameters::CreateContainerOptions; use bollard::Docker; use maplit::hashmap; use miette::{bail, IntoDiagnostic}; @@ -38,26 +38,26 @@ impl ServiceTrait for Kaspersky { } pull_image(docker, image).await?; let options = Some(CreateContainerOptions { - name: self.container_name(cloud_id).unwrap(), + name: self.container_name(cloud_id), ..CreateContainerOptions::default() }); - let config = Config { - image: Some(image), + let config = ContainerCreateBody { + image: Some(image.into()), host_config: Some(HostConfig { network_mode: Some(network.to_string()), ..Default::default() }), labels: Some(hashmap! { - "haze-type" => self.name(), - "haze-cloud-id" => cloud_id + "haze-type".into() => self.name().into(), + "haze-cloud-id".into() => cloud_id.into() }), networking_config: Some(NetworkingConfig { - endpoints_config: hashmap! { - network => EndpointSettings { + endpoints_config: Some(hashmap! { + network.into() => EndpointSettings { aliases: Some(vec![self.name().to_string()]), ..Default::default() } - }, + }), }), ..Default::default() }; @@ -66,10 +66,7 @@ impl ServiceTrait for Kaspersky { .await .into_diagnostic()? .id; - docker - .start_container::(&id, None) - .await - .into_diagnostic()?; + docker.start_container(&id, None).await.into_diagnostic()?; Ok(vec![id]) } @@ -145,26 +142,26 @@ impl ServiceTrait for KasperskyIcap { } pull_image(docker, image).await?; let options = Some(CreateContainerOptions { - name: self.container_name(cloud_id).unwrap(), + name: self.container_name(cloud_id), ..CreateContainerOptions::default() }); - let config = Config { - image: Some(image), + let config = ContainerCreateBody { + image: Some(image.into()), host_config: Some(HostConfig { network_mode: Some(network.to_string()), ..Default::default() }), labels: Some(hashmap! { - "haze-type" => self.name(), - "haze-cloud-id" => cloud_id + "haze-type".into() => self.name().into(), + "haze-cloud-id".into() => cloud_id.into(), }), networking_config: Some(NetworkingConfig { - endpoints_config: hashmap! { - network => EndpointSettings { + endpoints_config: Some(hashmap! { + network.into() => EndpointSettings { aliases: Some(vec![self.name().to_string()]), ..Default::default() } - }, + }), }), ..Default::default() }; @@ -173,10 +170,7 @@ impl ServiceTrait for KasperskyIcap { .await .into_diagnostic()? .id; - docker - .start_container::(&id, None) - .await - .into_diagnostic()?; + docker.start_container(&id, None).await.into_diagnostic()?; Ok(vec![id]) } diff --git a/src/service/ldap.rs b/src/service/ldap.rs index 4868ffe..7526918 100644 --- a/src/service/ldap.rs +++ b/src/service/ldap.rs @@ -3,8 +3,9 @@ use crate::config::HazeConfig; use crate::image::pull_image; use crate::service::ServiceTrait; use crate::Result; -use bollard::container::{Config, CreateContainerOptions, NetworkingConfig}; -use bollard::models::{ContainerState, EndpointSettings, HostConfig}; +use bollard::config::NetworkingConfig; +use bollard::models::{ContainerCreateBody, ContainerState, EndpointSettings, HostConfig}; +use bollard::query_parameters::CreateContainerOptions; use bollard::Docker; use maplit::hashmap; use miette::{IntoDiagnostic, Report}; @@ -33,29 +34,29 @@ impl ServiceTrait for Ldap { let image = "icewind1991/haze-ldap"; pull_image(docker, image).await?; let options = Some(CreateContainerOptions { - name: self.container_name(cloud_id).unwrap(), + name: self.container_name(cloud_id), ..CreateContainerOptions::default() }); - let config = Config { - image: Some(image), - env: Some(vec!["LDAP_ADMIN_PASSWORD=haze"]), + let config = ContainerCreateBody { + image: Some(image.into()), + env: Some(vec!["LDAP_ADMIN_PASSWORD=haze".into()]), host_config: Some(HostConfig { network_mode: Some(network.to_string()), ..Default::default() }), labels: Some(hashmap! { - "haze-type" => self.name(), - "haze-cloud-id" => cloud_id + "haze-type".into() => self.name().into(), + "haze-cloud-id".into() => cloud_id.into() }), networking_config: Some(NetworkingConfig { - endpoints_config: hashmap! { - network => EndpointSettings { + endpoints_config: Some(hashmap! { + network.into() => EndpointSettings { aliases: Some(vec![self.name().to_string()]), ..Default::default() } - }, + }), }), - cmd: Some(vec!["--copy-service"]), + cmd: Some(vec!["--copy-service".into()]), ..Default::default() }; let id = docker @@ -63,10 +64,7 @@ impl ServiceTrait for Ldap { .await .into_diagnostic()? .id; - docker - .start_container::(&id, None) - .await - .into_diagnostic()?; + docker.start_container(&id, None).await.into_diagnostic()?; Ok(vec![id]) } @@ -112,29 +110,29 @@ impl ServiceTrait for LdapAdmin { let image = "osixia/phpldapadmin"; pull_image(docker, image).await?; let options = Some(CreateContainerOptions { - name: self.container_name(cloud_id).unwrap(), + name: self.container_name(cloud_id), ..CreateContainerOptions::default() }); - let config = Config { - image: Some(image), - env: Some(vec!["PHPLDAPADMIN_LDAP_HOSTS=ldap"]), + let config = ContainerCreateBody { + image: Some(image.into()), + env: Some(vec!["PHPLDAPADMIN_LDAP_HOSTS=ldap".into()]), host_config: Some(HostConfig { network_mode: Some(network.to_string()), ..Default::default() }), labels: Some(hashmap! { - "haze-type" => self.name(), - "haze-cloud-id" => cloud_id + "haze-type".into() => self.name().into(), + "haze-cloud-id".into() => cloud_id.into(), }), networking_config: Some(NetworkingConfig { - endpoints_config: hashmap! { - network => EndpointSettings { + endpoints_config: Some(hashmap! { + network.into() => EndpointSettings { aliases: Some(vec![self.name().to_string()]), ..Default::default() } - }, + }), }), - cmd: Some(vec!["--copy-service"]), + cmd: Some(vec!["--copy-service".into()]), ..Default::default() }; let id = docker @@ -142,10 +140,7 @@ impl ServiceTrait for LdapAdmin { .await .into_diagnostic()? .id; - docker - .start_container::(&id, None) - .await - .into_diagnostic()?; + docker.start_container(&id, None).await.into_diagnostic()?; Ok(vec![id]) } diff --git a/src/service/mail.rs b/src/service/mail.rs index 93a411d..c52fdac 100644 --- a/src/service/mail.rs +++ b/src/service/mail.rs @@ -3,8 +3,8 @@ use crate::config::HazeConfig; use crate::image::pull_image; use crate::service::ServiceTrait; use crate::Result; -use bollard::container::{Config, CreateContainerOptions, NetworkingConfig}; -use bollard::models::{EndpointSettings, HostConfig}; +use bollard::models::{ContainerCreateBody, EndpointSettings, HostConfig, NetworkingConfig}; +use bollard::query_parameters::CreateContainerOptions; use bollard::Docker; use maplit::hashmap; use miette::IntoDiagnostic; @@ -29,26 +29,26 @@ impl ServiceTrait for Mail { let image = "rnwood/smtp4dev"; pull_image(docker, image).await?; let options = Some(CreateContainerOptions { - name: self.container_name(cloud_id).unwrap(), + name: self.container_name(cloud_id), ..CreateContainerOptions::default() }); - let config = Config { - image: Some(image), + let config = ContainerCreateBody { + image: Some(image.into()), host_config: Some(HostConfig { network_mode: Some(network.to_string()), ..Default::default() }), labels: Some(hashmap! { - "haze-type" => self.name(), - "haze-cloud-id" => cloud_id + "haze-type".into() => self.name().into(), + "haze-cloud-id".into() => cloud_id.into(), }), networking_config: Some(NetworkingConfig { - endpoints_config: hashmap! { - network => EndpointSettings { + endpoints_config: Some(hashmap! { + network.into() => EndpointSettings { aliases: Some(vec![self.name().to_string()]), ..Default::default() } - }, + }), }), ..Default::default() }; @@ -57,10 +57,7 @@ impl ServiceTrait for Mail { .await .into_diagnostic()? .id; - docker - .start_container::(&id, None) - .await - .into_diagnostic()?; + docker.start_container(&id, None).await.into_diagnostic()?; Ok(vec![id]) } diff --git a/src/service/objectstore.rs b/src/service/objectstore.rs index 6522207..2225dca 100644 --- a/src/service/objectstore.rs +++ b/src/service/objectstore.rs @@ -4,8 +4,10 @@ use crate::exec::exec; use crate::image::pull_image; use crate::service::ServiceTrait; use crate::Result; -use bollard::container::{Config, CreateContainerOptions, NetworkingConfig}; -use bollard::models::{ContainerState, EndpointSettings, HostConfig}; +use bollard::models::{ + ContainerCreateBody, ContainerState, EndpointSettings, HostConfig, NetworkingConfig, +}; +use bollard::query_parameters::CreateContainerOptions; use bollard::Docker; use maplit::hashmap; use miette::IntoDiagnostic; @@ -82,28 +84,28 @@ impl ServiceTrait for ObjectStore { ) -> Result> { pull_image(docker, self.image()).await?; let options = Some(CreateContainerOptions { - name: format!("{}-object", cloud_id), + name: Some(format!("{}-object", cloud_id)), ..CreateContainerOptions::default() }); - let config = Config { - image: Some(self.image()), - env: Some(self.self_env()), + let config = ContainerCreateBody { + image: Some(self.image().into()), + env: Some(self.self_env().into_iter().map(String::from).collect()), host_config: Some(HostConfig { network_mode: Some(network.to_string()), ..Default::default() }), labels: Some(hashmap! { - "haze-type" => self.name(), - "haze-cloud-id" => cloud_id + "haze-type".into() => self.name().into(), + "haze-cloud-id".into() => cloud_id.into(), }), - cmd: Some(self.args().into()), + cmd: Some(self.args().iter().copied().map(String::from).collect()), networking_config: Some(NetworkingConfig { - endpoints_config: hashmap! { - network => EndpointSettings { + endpoints_config: Some(hashmap! { + network.into() => EndpointSettings { aliases: Some(vec![self.host_name().to_string()]), ..Default::default() } - }, + }), }), ..Default::default() }; @@ -112,10 +114,7 @@ impl ServiceTrait for ObjectStore { .await .into_diagnostic()? .id; - docker - .start_container::(&id, None) - .await - .into_diagnostic()?; + docker.start_container(&id, None).await.into_diagnostic()?; Ok(vec![id]) } diff --git a/src/service/oc.rs b/src/service/oc.rs index fcff9e6..2eb3a22 100644 --- a/src/service/oc.rs +++ b/src/service/oc.rs @@ -4,8 +4,9 @@ use crate::exec::exec; use crate::image::pull_image; use crate::service::ServiceTrait; use crate::Result; -use bollard::container::{Config, CreateContainerOptions, NetworkingConfig}; -use bollard::models::{EndpointSettings, HostConfig}; +use bollard::config::NetworkingConfig; +use bollard::models::{ContainerCreateBody, EndpointSettings, HostConfig}; +use bollard::query_parameters::CreateContainerOptions; use bollard::Docker; use maplit::hashmap; use miette::IntoDiagnostic; @@ -33,7 +34,7 @@ impl ServiceTrait for Oc { let image = "owncloud/server:10.12.2"; pull_image(docker, image).await?; let options = Some(CreateContainerOptions { - name: self.container_name(cloud_id).unwrap(), + name: self.container_name(cloud_id), ..CreateContainerOptions::default() }); let addr = config.proxy.addr( @@ -43,24 +44,24 @@ impl ServiceTrait for Oc { let domain = addr.split_once("://").unwrap().1; let env_trusted_domain = format!("OWNCLOUD_TRUSTED_DOMAINS={domain}"); let env_domain = format!("OWNCLOUD_DOMAIN={domain}"); - let config = Config { - image: Some(image), + let config = ContainerCreateBody { + image: Some(image.into()), host_config: Some(HostConfig { network_mode: Some(network.to_string()), ..Default::default() }), - env: Some(vec![&env_trusted_domain, &env_domain]), + env: Some(vec![env_trusted_domain, env_domain]), labels: Some(hashmap! { - "haze-type" => self.name(), - "haze-cloud-id" => cloud_id + "haze-type".into() => self.name().into(), + "haze-cloud-id".into() => cloud_id.into(), }), networking_config: Some(NetworkingConfig { - endpoints_config: hashmap! { - network => EndpointSettings { + endpoints_config: Some(hashmap! { + network.into() => EndpointSettings { aliases: Some(vec![self.name().to_string()]), ..Default::default() } - }, + }), }), ..Default::default() }; @@ -69,10 +70,7 @@ impl ServiceTrait for Oc { .await .into_diagnostic()? .id; - docker - .start_container::(&id, None) - .await - .into_diagnostic()?; + docker.start_container(&id, None).await.into_diagnostic()?; Ok(vec![id]) } diff --git a/src/service/office.rs b/src/service/office.rs index 87266a6..4cd72c9 100644 --- a/src/service/office.rs +++ b/src/service/office.rs @@ -3,8 +3,10 @@ use crate::config::HazeConfig; use crate::image::pull_image; use crate::service::ServiceTrait; use crate::Result; -use bollard::container::{Config, CreateContainerOptions, NetworkingConfig}; -use bollard::models::{ContainerState, EndpointSettings, HostConfig}; +use bollard::models::{ + ContainerCreateBody, ContainerState, EndpointSettings, HostConfig, NetworkingConfig, +}; +use bollard::query_parameters::CreateContainerOptions; use bollard::Docker; use maplit::hashmap; use miette::{IntoDiagnostic, Report}; @@ -34,7 +36,7 @@ impl ServiceTrait for Office { pull_image(docker, image).await?; let container_id = self.container_name(cloud_id).unwrap(); let options = Some(CreateContainerOptions { - name: container_id.clone(), + name: Some(container_id.clone()), ..CreateContainerOptions::default() }); let mut env = vec!["extra_params=--o:ssl.enable=false --o:ssl.termination=true"]; @@ -54,24 +56,24 @@ impl ServiceTrait for Office { env.push(&server_name_opt); } - let config = Config { - image: Some(image), - env: Some(env), + let config = ContainerCreateBody { + image: Some(image.into()), + env: Some(env.into_iter().map(String::from).collect()), host_config: Some(HostConfig { network_mode: Some(network.to_string()), ..Default::default() }), labels: Some(hashmap! { - "haze-type" => self.name(), - "haze-cloud-id" => cloud_id + "haze-type".into() => self.name().into(), + "haze-cloud-id".into() => cloud_id.into(), }), networking_config: Some(NetworkingConfig { - endpoints_config: hashmap! { - network => EndpointSettings { + endpoints_config: Some(hashmap! { + network.into() => EndpointSettings { aliases: Some(vec![self.name().to_string()]), ..Default::default() } - }, + }), }), ..Default::default() }; @@ -80,10 +82,7 @@ impl ServiceTrait for Office { .await .into_diagnostic()? .id; - docker - .start_container::(&id, None) - .await - .into_diagnostic()?; + docker.start_container(&id, None).await.into_diagnostic()?; Ok(vec![id]) } diff --git a/src/service/onlyoffice.rs b/src/service/onlyoffice.rs index f262c8d..b7c8a14 100644 --- a/src/service/onlyoffice.rs +++ b/src/service/onlyoffice.rs @@ -4,8 +4,10 @@ use crate::exec::exec; use crate::image::pull_image; use crate::service::ServiceTrait; use crate::Result; -use bollard::container::{Config, CreateContainerOptions, NetworkingConfig}; -use bollard::models::{ContainerState, EndpointSettings, HostConfig}; +use bollard::models::{ + ContainerCreateBody, ContainerState, EndpointSettings, HostConfig, NetworkingConfig, +}; +use bollard::query_parameters::CreateContainerOptions; use bollard::Docker; use maplit::hashmap; use miette::{IntoDiagnostic, Report}; @@ -35,26 +37,26 @@ impl ServiceTrait for OnlyOffice { let image = "onlyoffice/documentserver"; pull_image(docker, image).await?; let options = Some(CreateContainerOptions { - name: self.container_name(cloud_id).unwrap(), + name: self.container_name(cloud_id), ..CreateContainerOptions::default() }); - let config = Config { - image: Some(image), + let config = ContainerCreateBody { + image: Some(image.into()), host_config: Some(HostConfig { network_mode: Some(network.to_string()), ..Default::default() }), labels: Some(hashmap! { - "haze-type" => self.name(), - "haze-cloud-id" => cloud_id + "haze-type".into() => self.name().into(), + "haze-cloud-id".into() => cloud_id.into(), }), networking_config: Some(NetworkingConfig { - endpoints_config: hashmap! { - network => EndpointSettings { + endpoints_config: Some(hashmap! { + network.into() => EndpointSettings { aliases: Some(vec![self.name().to_string()]), ..Default::default() } - }, + }), }), ..Default::default() }; @@ -63,10 +65,7 @@ impl ServiceTrait for OnlyOffice { .await .into_diagnostic()? .id; - docker - .start_container::(&id, None) - .await - .into_diagnostic()?; + docker.start_container(&id, None).await.into_diagnostic()?; Ok(vec![id]) } diff --git a/src/service/push.rs b/src/service/push.rs index ed4bf57..22b3ac2 100644 --- a/src/service/push.rs +++ b/src/service/push.rs @@ -2,8 +2,8 @@ use crate::cloud::CloudOptions; use crate::config::HazeConfig; use crate::image::pull_image; use crate::service::ServiceTrait; -use bollard::container::{Config, CreateContainerOptions, NetworkingConfig}; -use bollard::models::{EndpointSettings, HostConfig}; +use bollard::models::{ContainerCreateBody, EndpointSettings, HostConfig, NetworkingConfig}; +use bollard::query_parameters::CreateContainerOptions; use bollard::Docker; use local_ip_address::list_afinet_netifas; use maplit::hashmap; @@ -33,11 +33,11 @@ impl ServiceTrait for NotifyPush { let image = "icewind1991/notify_push"; pull_image(docker, image).await?; let options = Some(CreateContainerOptions { - name: self.container_name(cloud_id).unwrap(), + name: self.container_name(cloud_id), ..CreateContainerOptions::default() }); - let config = Config { - image: Some(image), + let config = ContainerCreateBody { + image: Some(image.into()), host_config: Some(HostConfig { network_mode: Some(network.to_string()), binds: Some(vec![ @@ -47,23 +47,23 @@ impl ServiceTrait for NotifyPush { ..Default::default() }), env: Some(vec![ - "NEXTCLOUD_URL=http://cloud/", - "LOG=debug", - "REDIS_URL=redis://cloud/", + "NEXTCLOUD_URL=http://cloud/".into(), + "LOG=debug".into(), + "REDIS_URL=redis://cloud/".into(), ]), labels: Some(hashmap! { - "haze-type" => self.name(), - "haze-cloud-id" => cloud_id + "haze-type".into() => self.name().into(), + "haze-cloud-id".into() => cloud_id.into(), }), networking_config: Some(NetworkingConfig { - endpoints_config: hashmap! { - network => EndpointSettings { + endpoints_config: Some(hashmap! { + network.into() => EndpointSettings { aliases: Some(vec![self.name().to_string()]), ..Default::default() } - }, + }), }), - cmd: Some(vec!["/notify_push", "/config/config.php"]), + cmd: Some(vec!["/notify_push".into(), "/config/config.php".into()]), ..Default::default() }; let id = docker diff --git a/src/service/redis.rs b/src/service/redis.rs index 1712b9b..29a22ee 100644 --- a/src/service/redis.rs +++ b/src/service/redis.rs @@ -3,8 +3,8 @@ use crate::config::HazeConfig; use crate::image::pull_image; use crate::service::ServiceTrait; use crate::Result; -use bollard::container::{Config, CreateContainerOptions, NetworkingConfig}; -use bollard::models::{EndpointSettings, HostConfig}; +use bollard::models::{ContainerCreateBody, EndpointSettings, HostConfig, NetworkingConfig}; +use bollard::query_parameters::CreateContainerOptions; use bollard::Docker; use maplit::hashmap; use miette::IntoDiagnostic; @@ -29,26 +29,26 @@ impl ServiceTrait for Redis { let image = "redis:8-alpine"; pull_image(docker, image).await?; let options = Some(CreateContainerOptions { - name: self.container_name(cloud_id).unwrap(), + name: self.container_name(cloud_id), ..CreateContainerOptions::default() }); - let config = Config { - image: Some(image), + let config = ContainerCreateBody { + image: Some(image.into()), host_config: Some(HostConfig { network_mode: Some(network.to_string()), ..Default::default() }), labels: Some(hashmap! { - "haze-type" => self.name(), - "haze-cloud-id" => cloud_id + "haze-type".into() => self.name().into(), + "haze-cloud-id".into() => cloud_id.into(), }), networking_config: Some(NetworkingConfig { - endpoints_config: hashmap! { - network => EndpointSettings { + endpoints_config: Some(hashmap! { + network.into() => EndpointSettings { aliases: Some(vec![self.name().to_string()]), ..Default::default() } - }, + }), }), ..Default::default() }; @@ -57,10 +57,7 @@ impl ServiceTrait for Redis { .await .into_diagnostic()? .id; - docker - .start_container::(&id, None) - .await - .into_diagnostic()?; + docker.start_container(&id, None).await.into_diagnostic()?; Ok(vec![id]) } diff --git a/src/service/sftp.rs b/src/service/sftp.rs index 84502c1..80405e2 100644 --- a/src/service/sftp.rs +++ b/src/service/sftp.rs @@ -3,8 +3,8 @@ use crate::config::HazeConfig; use crate::image::pull_image; use crate::service::ServiceTrait; use crate::Result; -use bollard::container::{Config, CreateContainerOptions, NetworkingConfig}; -use bollard::models::{EndpointSettings, HostConfig}; +use bollard::models::{ContainerCreateBody, EndpointSettings, HostConfig, NetworkingConfig}; +use bollard::query_parameters::CreateContainerOptions; use bollard::Docker; use maplit::hashmap; use miette::IntoDiagnostic; @@ -29,28 +29,28 @@ impl ServiceTrait for Sftp { let image = "atmoz/sftp:alpine"; pull_image(docker, image).await?; let options = Some(CreateContainerOptions { - name: self.container_name(cloud_id).unwrap(), + name: self.container_name(cloud_id), ..CreateContainerOptions::default() }); - let config = Config { - image: Some(image), + let config = ContainerCreateBody { + image: Some(image.into()), host_config: Some(HostConfig { network_mode: Some(network.to_string()), ..Default::default() }), labels: Some(hashmap! { - "haze-type" => self.name(), - "haze-cloud-id" => cloud_id + "haze-type".into() => self.name().into(), + "haze-cloud-id".into() => cloud_id.into(), }), networking_config: Some(NetworkingConfig { - endpoints_config: hashmap! { - network => EndpointSettings { + endpoints_config: Some(hashmap! { + network.into() => EndpointSettings { aliases: Some(vec![self.name().to_string()]), ..Default::default() } - }, + }), }), - cmd: Some(vec!["test:test:::data"]), + cmd: Some(vec!["test:test:::data".into()]), ..Default::default() }; let id = docker @@ -58,10 +58,7 @@ impl ServiceTrait for Sftp { .await .into_diagnostic()? .id; - docker - .start_container::(&id, None) - .await - .into_diagnostic()?; + docker.start_container(&id, None).await.into_diagnostic()?; Ok(vec![id]) } diff --git a/src/service/smb.rs b/src/service/smb.rs index 5f4dec9..a40b105 100644 --- a/src/service/smb.rs +++ b/src/service/smb.rs @@ -3,8 +3,8 @@ use crate::config::HazeConfig; use crate::image::pull_image; use crate::service::ServiceTrait; use crate::Result; -use bollard::container::{Config, CreateContainerOptions, NetworkingConfig}; -use bollard::models::{EndpointSettings, HostConfig}; +use bollard::models::{ContainerCreateBody, EndpointSettings, HostConfig, NetworkingConfig}; +use bollard::query_parameters::CreateContainerOptions; use bollard::Docker; use maplit::hashmap; use miette::IntoDiagnostic; @@ -29,31 +29,31 @@ impl ServiceTrait for Smb { let image = "ghcr.io/servercontainers/samba:smbd-only-a3.18.0-s4.18.2-r0"; pull_image(docker, image).await?; let options = Some(CreateContainerOptions { - name: self.container_name(cloud_id).unwrap(), + name: self.container_name(cloud_id), ..CreateContainerOptions::default() }); - let config = Config { - image: Some(image), + let config = ContainerCreateBody { + image: Some(image.into()), host_config: Some(HostConfig { network_mode: Some(network.to_string()), ..Default::default() }), env: Some(vec![ - "ACCOUNT_test=test", - "UID_test=1000", - "SAMBA_VOLUME_CONFIG_test=[test]; path=/tmp; valid users = test; guest ok = no; read only = no; browseable = yes", + "ACCOUNT_test=test".into(), + "UID_test=1000".into(), + "SAMBA_VOLUME_CONFIG_test=[test]; path=/tmp; valid users = test; guest ok = no; read only = no; browseable = yes".into(), ]), labels: Some(hashmap! { - "haze-type" => self.name(), - "haze-cloud-id" => cloud_id + "haze-type".into() => self.name().into(), + "haze-cloud-id".into() => cloud_id.into(), }), networking_config: Some(NetworkingConfig { - endpoints_config: hashmap! { - network => EndpointSettings { + endpoints_config: Some(hashmap! { + network.into() => EndpointSettings { aliases: Some(vec![self.name().to_string()]), ..Default::default() } - }, + }), }), ..Default::default() }; @@ -62,10 +62,7 @@ impl ServiceTrait for Smb { .await .into_diagnostic()? .id; - docker - .start_container::(&id, None) - .await - .into_diagnostic()?; + docker.start_container(&id, None).await.into_diagnostic()?; Ok(vec![id]) } From 2ea01b6570554e7ec6524c8ea028942c4166c645 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Fri, 27 Feb 2026 22:56:08 +0100 Subject: [PATCH 08/68] cargo update --- Cargo.lock | 732 +++++++++++++++++++++++++++++++++++++---------------- Cargo.toml | 44 ++-- 2 files changed, 533 insertions(+), 243 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 74b1270..1b97063 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -86,13 +86,13 @@ checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f" [[package]] name = "async-trait" -version = "0.1.87" +version = "0.1.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d556ec1359574147ec0c4fc5eb525f3f23263a592b1a9c07e0a75b427de55c97" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -119,10 +119,32 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] -name = "axum" -version = "0.8.1" +name = "aws-lc-rs" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d6fd624c75e18b3b4c6b9caf42b1afe24437daaee904069137d8bab077be8b8" +checksum = "d9a7b350e3bb1767102698302bc37256cbd48422809984b98d292c40e2579aa9" +dependencies = [ + "aws-lc-sys", + "zeroize", +] + +[[package]] +name = "aws-lc-sys" +version = "0.37.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b092fe214090261288111db7a2b2c2118e5a7f30dc2569f1732c4069a6840549" +dependencies = [ + "cc", + "cmake", + "dunce", + "fs_extra", +] + +[[package]] +name = "axum" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b52af3cb4058c895d37317bb27508dccc8e5f2d39454016b297bf4a400597b8" dependencies = [ "axum-core", "bytes", @@ -139,8 +161,7 @@ dependencies = [ "mime", "percent-encoding", "pin-project-lite", - "rustversion", - "serde", + "serde_core", "serde_json", "serde_path_to_error", "serde_urlencoded", @@ -154,18 +175,17 @@ dependencies = [ [[package]] name = "axum-core" -version = "0.5.0" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df1362f362fd16024ae199c1970ce98f9661bf5ef94b9808fee734bc3698b733" +checksum = "08c78f31d7b1291f7ee735c1c6780ccde7785daae9a9206026862dab7d8792d1" dependencies = [ "bytes", - "futures-util", + "futures-core", "http", "http-body", "http-body-util", "mime", "pin-project-lite", - "rustversion", "sync_wrapper", "tower-layer", "tower-service", @@ -315,24 +335,31 @@ dependencies = [ [[package]] name = "camino" -version = "1.1.9" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b96ec4966b5813e2c0507c1f86115c8c5abaadc3980879c3424042a02fd1ad3" +checksum = "e629a66d692cb9ff1a1c664e41771b3dcaf961985a9774c0eb0bd1b51cf60a48" dependencies = [ - "serde", + "serde_core", ] [[package]] name = "cc" -version = "1.2.16" +version = "1.2.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be714c154be609ec7f5dad223a33bf1482fff90472de28f7362806e6d4832b8c" +checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2" dependencies = [ + "find-msvc-tools", "jobserver", "libc", "shlex", ] +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + [[package]] name = "cfg-if" version = "1.0.0" @@ -386,7 +413,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -395,12 +422,31 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" +[[package]] +name = "cmake" +version = "0.1.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75443c44cd6b379beb8c5b45d85d0773baf31cce901fe7bb252f4eff3008ef7d" +dependencies = [ + "cc", +] + [[package]] name = "colorchoice" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" +[[package]] +name = "combine" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "memchr", +] + [[package]] name = "console" version = "0.16.2" @@ -480,14 +526,38 @@ dependencies = [ ] [[package]] -name = "dbus" -version = "0.9.7" +name = "darling" +version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bb21987b9fb1613058ba3843121dd18b163b254d8a6e797e144cbac14d96d1b" +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" dependencies = [ - "libc", - "libdbus-sys", - "winapi", + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" +dependencies = [ + "darling_core", + "quote", + "syn", ] [[package]] @@ -505,6 +575,37 @@ dependencies = [ "powerfmt", ] +[[package]] +name = "derive_builder" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947" +dependencies = [ + "derive_builder_macro", +] + +[[package]] +name = "derive_builder_core" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "derive_builder_macro" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" +dependencies = [ + "derive_builder_core", + "syn", +] + [[package]] name = "digest" version = "0.10.7" @@ -556,9 +657,15 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + [[package]] name = "either" version = "1.14.0" @@ -580,7 +687,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -612,14 +719,20 @@ dependencies = [ ] [[package]] -name = "flate2" -version = "1.1.0" +name = "find-msvc-tools" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11faaf5a5236997af9848be0bef4db95824b1d534ebc64d0f0c6cf3e67bd38dc" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "flate2" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" dependencies = [ "crc32fast", - "libz-rs-sys", "miniz_oxide", + "zlib-rs", ] [[package]] @@ -643,6 +756,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fs_extra" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" + [[package]] name = "futures-channel" version = "0.3.31" @@ -654,19 +773,19 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" [[package]] name = "futures-macro" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -677,21 +796,20 @@ checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" [[package]] name = "futures-task" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" [[package]] name = "futures-util" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" dependencies = [ "futures-core", "futures-macro", "futures-task", "pin-project-lite", - "pin-utils", "slab", ] @@ -747,6 +865,18 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "getset" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf0fc11e47561d47397154977bc219f4cf809b2974facc3ccb3b89e2436f912" +dependencies = [ + "proc-macro-error2", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "gimli" version = "0.31.1" @@ -755,9 +885,9 @@ checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] name = "git2" -version = "0.20.0" +version = "0.20.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fda788993cc341f69012feba8bf45c0ba4f3291fcc08e214b4d5a7332d88aff" +checksum = "7b88256088d75a56f8ecfa070513a775dd9107f6530ef14919dac831af9cfe2b" dependencies = [ "bitflags", "libc", @@ -934,13 +1064,14 @@ dependencies = [ [[package]] name = "hyper" -version = "1.6.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" +checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" dependencies = [ + "atomic-waker", "bytes", "futures-channel", - "futures-util", + "futures-core", "h2", "http", "http-body", @@ -948,6 +1079,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", + "pin-utils", "smallvec", "tokio", "want", @@ -989,7 +1121,6 @@ dependencies = [ "hyper", "hyper-util", "rustls", - "rustls-native-certs", "rustls-pki-types", "tokio", "tokio-rustls", @@ -998,18 +1129,22 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.10" +version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4" +checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" dependencies = [ + "base64", "bytes", "futures-channel", "futures-util", "http", "http-body", "hyper", + "ipnet", + "libc", + "percent-encoding", "pin-project-lite", - "socket2 0.5.8", + "socket2", "tokio", "tower-service", "tracing", @@ -1145,7 +1280,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -1154,6 +1289,12 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + [[package]] name = "idna" version = "1.0.3" @@ -1215,6 +1356,16 @@ version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" +[[package]] +name = "iri-string" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c91338f0783edbd6195decb37bae672fd3b165faffb89bf7b9e6942f8b1a731a" +dependencies = [ + "memchr", + "serde", +] + [[package]] name = "is-terminal" version = "0.4.16" @@ -1253,6 +1404,28 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" +[[package]] +name = "jni" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +dependencies = [ + "cesu8", + "cfg-if", + "combine", + "jni-sys", + "log", + "thiserror 1.0.69", + "walkdir", + "windows-sys 0.45.0", +] + +[[package]] +name = "jni-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" + [[package]] name = "jobserver" version = "0.1.32" @@ -1296,21 +1469,11 @@ version = "0.2.182" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112" -[[package]] -name = "libdbus-sys" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06085512b750d640299b79be4bad3d2fa90a9c00b1fd9e1b46364f66f0485c72" -dependencies = [ - "cc", - "pkg-config", -] - [[package]] name = "libgit2-sys" -version = "0.18.0+1.9.0" +version = "0.18.3+1.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1a117465e7e1597e8febea8bb0c410f1c7fb93b1e1cddf34363f8390367ffec" +checksum = "c9b3acc4b91781bb0b3386669d325163746af5f6e4f73e6d2d630e09a35f3487" dependencies = [ "cc", "libc", @@ -1329,15 +1492,6 @@ dependencies = [ "redox_syscall", ] -[[package]] -name = "libz-rs-sys" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "902bc563b5d65ad9bba616b490842ef0651066a1a1dc3ce1087113ffcb873c8d" -dependencies = [ - "zlib-rs", -] - [[package]] name = "libz-sys" version = "1.1.21" @@ -1364,14 +1518,22 @@ checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856" [[package]] name = "local-ip-address" -version = "0.6.5" +version = "0.6.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "656b3b27f8893f7bbf9485148ff9a65f019e3f33bd5cdc87c83cab16b3fd9ec8" +checksum = "79ef8c257c92ade496781a32a581d43e3d512cf8ce714ecf04ea80f93ed0ff4a" dependencies = [ "libc", "neli", - "thiserror 2.0.12", - "windows-sys 0.59.0", + "windows-sys 0.61.2", +] + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", ] [[package]] @@ -1415,9 +1577,9 @@ checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "miette" -version = "7.5.0" +version = "7.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a955165f87b37fd1862df2a59547ac542c77ef6d17c666f619d1ad22dd89484" +checksum = "5f98efec8807c63c752b5bd61f862c165c115b0a35685bdcfd9238c7aeb592b7" dependencies = [ "backtrace", "backtrace-ext", @@ -1429,19 +1591,18 @@ dependencies = [ "supports-unicode", "terminal_size", "textwrap", - "thiserror 1.0.69", "unicode-width 0.1.14", ] [[package]] name = "miette-derive" -version = "7.5.0" +version = "7.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf45bf44ab49be92fd1227a3be6fc6f617f1a337c06af54981048574d8783147" +checksum = "db5b29714e950dbb20d5e6f74f9dcec4edbcc1067bb7f8ed198c097b8c1a818b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -1457,6 +1618,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e3e04debbb59698c15bacbb6d93584a8c0ca9cc3213cb423d31f760d8843ce5" dependencies = [ "adler2", + "simd-adler32", ] [[package]] @@ -1472,27 +1634,31 @@ dependencies = [ [[package]] name = "neli" -version = "0.6.5" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93062a0dce6da2517ea35f301dfc88184ce18d3601ec786a727a87bf535deca9" +checksum = "22f9786d56d972959e1408b6a93be6af13b9c1392036c5c1fafa08a1b0c6ee87" dependencies = [ + "bitflags", "byteorder", + "derive_builder", + "getset", "libc", "log", "neli-proc-macros", + "parking_lot", ] [[package]] name = "neli-proc-macros" -version = "0.1.4" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c8034b7fbb6f9455b2a96c19e6edf8dc9fc34c70449938d8ee3b4df363f61fe" +checksum = "05d8d08c6e98f20a62417478ebf7be8e1425ec9acecc6f63e22da633f6b71609" dependencies = [ "either", "proc-macro2", "quote", "serde", - "syn 1.0.109", + "syn", ] [[package]] @@ -1506,12 +1672,11 @@ dependencies = [ [[package]] name = "nu-ansi-term" -version = "0.46.0" +version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "overload", - "winapi", + "windows-sys 0.61.2", ] [[package]] @@ -1543,14 +1708,13 @@ checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e" [[package]] name = "opener" -version = "0.7.2" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0812e5e4df08da354c851a3376fead46db31c2214f849d3de356d774d057681" +checksum = "a2fa337e0cf13357c13ef1dc108df1333eb192f75fc170bea03fcf1fd404c2ee" dependencies = [ "bstr", - "dbus", "normpath", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -1559,22 +1723,39 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" -[[package]] -name = "overload" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" - [[package]] name = "owo-colors" -version = "4.2.2" +version = "4.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48dd4f4a2c8405440fd0462561f0e5806bd0f77e86f51c761481bdd4018b545e" +checksum = "d211803b9b6b570f68772237e415a029d5a50c65d382910b879fb19d3271f94d" dependencies = [ "supports-color 2.1.0", "supports-color 3.0.2", ] +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link", +] + [[package]] name = "pbkdf2" version = "0.12.2" @@ -1657,7 +1838,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", - "syn 2.0.117", + "syn", +] + +[[package]] +name = "proc-macro-error-attr2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "proc-macro-error2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" +dependencies = [ + "proc-macro-error-attr2", + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -1682,7 +1885,7 @@ dependencies = [ "quinn-udp", "rustc-hash", "rustls", - "socket2 0.5.8", + "socket2", "thiserror 2.0.12", "tokio", "tracing", @@ -1695,6 +1898,7 @@ version = "0.11.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31" dependencies = [ + "aws-lc-rs", "bytes", "getrandom 0.3.4", "lru-slab", @@ -1719,9 +1923,9 @@ dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2 0.5.8", + "socket2", "tracing", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -1807,12 +2011,6 @@ dependencies = [ "bitflags", ] -[[package]] -name = "redox_termios" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20145670ba436b55d91fc92d25e71160fbfbdd57831631c8d7d36377a476f1cb" - [[package]] name = "redox_users" version = "0.4.6" @@ -1832,45 +2030,37 @@ checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" [[package]] name = "reqwest" -version = "0.12.12" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43e734407157c3c2034e0258f5e4473ddb361b1e85f95a66690d67264d7cd1da" +checksum = "ab3f43e3283ab1488b624b44b0e988d0acea0b3214e694730a055cb6b2efa801" dependencies = [ "base64", "bytes", "futures-core", - "futures-util", "http", "http-body", "http-body-util", "hyper", "hyper-rustls", "hyper-util", - "ipnet", "js-sys", "log", - "mime", - "once_cell", "percent-encoding", "pin-project-lite", "quinn", "rustls", - "rustls-native-certs", - "rustls-pemfile", "rustls-pki-types", - "serde", - "serde_json", - "serde_urlencoded", + "rustls-platform-verifier", "sync_wrapper", "tokio", "tokio-rustls", "tower", + "tower-http", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "windows-registry", ] [[package]] @@ -1918,8 +2108,8 @@ version = "0.23.36" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c665f33d38cea657d9614f766881e4d510e0eda4239891eea56b4cadcf01801b" dependencies = [ + "aws-lc-rs", "once_cell", - "ring", "rustls-pki-types", "rustls-webpki", "subtle", @@ -1938,15 +2128,6 @@ dependencies = [ "security-framework", ] -[[package]] -name = "rustls-pemfile" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" -dependencies = [ - "rustls-pki-types", -] - [[package]] name = "rustls-pki-types" version = "1.14.0" @@ -1957,12 +2138,40 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rustls-platform-verifier" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d99feebc72bae7ab76ba994bb5e121b8d83d910ca40b36e0921f53becc41784" +dependencies = [ + "core-foundation", + "core-foundation-sys", + "jni", + "log", + "once_cell", + "rustls", + "rustls-native-certs", + "rustls-platform-verifier-android", + "rustls-webpki", + "security-framework", + "security-framework-sys", + "webpki-root-certs", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustls-platform-verifier-android" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" + [[package]] name = "rustls-webpki" version = "0.103.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7df23109aa6c1567d1c575b9952556388da57401e4ace1d15f79eedad0d8f53" dependencies = [ + "aws-lc-rs", "ring", "rustls-pki-types", "untrusted", @@ -1980,6 +2189,15 @@ version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + [[package]] name = "schannel" version = "0.1.28" @@ -1989,6 +2207,12 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + [[package]] name = "security-framework" version = "3.6.0" @@ -2045,19 +2269,20 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] name = "serde_json" -version = "1.0.140" +version = "1.0.149" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" dependencies = [ "itoa", "memchr", - "ryu", "serde", + "serde_core", + "zmij", ] [[package]] @@ -2078,7 +2303,7 @@ checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -2146,9 +2371,9 @@ dependencies = [ [[package]] name = "shell-words" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" +checksum = "dc6fe69c597f9c37bfeeeeeb33da3530379845f10be461a66d16d03eca2ded77" [[package]] name = "shlex" @@ -2186,16 +2411,6 @@ version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" -[[package]] -name = "socket2" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] - [[package]] name = "socket2" version = "0.6.2" @@ -2220,23 +2435,23 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "strum" -version = "0.27.2" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" +checksum = "9628de9b8791db39ceda2b119bbe13134770b56c138ec1d3af810d045c04f9bd" dependencies = [ "strum_macros", ] [[package]] name = "strum_macros" -version = "0.27.2" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7" +checksum = "ab85eea0270ee17587ed4156089e10b9e6880ee688791d45a905f5b1ca36f664" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -2276,17 +2491,6 @@ version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7401a30af6cb5818bb64852270bb722533397edcfc7344954a38f420819ece2" -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - [[package]] name = "syn" version = "2.0.117" @@ -2315,7 +2519,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -2341,14 +2545,12 @@ dependencies = [ [[package]] name = "termion" -version = "4.0.4" +version = "4.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f359c854fbecc1ea65bc3683f1dcb2dce78b174a1ca7fda37acd1fff81df6ff" +checksum = "f44138a9ae08f0f502f24104d82517ef4da7330c35acd638f1f29d3cd5475ecb" dependencies = [ "libc", - "libredox", "numtoa", - "redox_termios", ] [[package]] @@ -2387,7 +2589,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -2398,7 +2600,7 @@ checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -2467,7 +2669,7 @@ dependencies = [ "mio", "pin-project-lite", "signal-hook-registry", - "socket2 0.6.2", + "socket2", "tokio-macros", "windows-sys 0.61.2", ] @@ -2480,7 +2682,7 @@ checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -2561,6 +2763,24 @@ dependencies = [ "tracing", ] +[[package]] +name = "tower-http" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" +dependencies = [ + "bitflags", + "bytes", + "futures-util", + "http", + "http-body", + "iri-string", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", +] + [[package]] name = "tower-layer" version = "0.3.3" @@ -2575,9 +2795,9 @@ checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" -version = "0.1.41" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" dependencies = [ "log", "pin-project-lite", @@ -2587,20 +2807,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.28" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] name = "tracing-core" -version = "0.1.33" +version = "0.1.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" dependencies = [ "once_cell", "valuable", @@ -2619,9 +2839,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.19" +version = "0.3.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" +checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e" dependencies = [ "nu-ansi-term", "sharded-slab", @@ -2738,6 +2958,16 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + [[package]] name = "want" version = "0.3.1" @@ -2793,7 +3023,7 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn 2.0.117", + "syn", "wasm-bindgen-shared", ] @@ -2828,7 +3058,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -2896,6 +3126,15 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "webpki-root-certs" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "804f18a4ac2676ffb4e8b5b5fa9ae38af06df08162314f96a68d2a363e21a8ca" +dependencies = [ + "rustls-pki-types", +] + [[package]] name = "winapi" version = "0.3.9" @@ -2912,6 +3151,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys 0.61.2", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" @@ -2925,33 +3173,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" [[package]] -name = "windows-registry" -version = "0.2.0" +name = "windows-sys" +version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" dependencies = [ - "windows-result", - "windows-strings", - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-result" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" -dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-strings" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" -dependencies = [ - "windows-result", - "windows-targets 0.52.6", + "windows-targets 0.42.2", ] [[package]] @@ -2990,6 +3217,21 @@ dependencies = [ "windows-link", ] +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + [[package]] name = "windows-targets" version = "0.52.6" @@ -3023,6 +3265,12 @@ dependencies = [ "windows_x86_64_msvc 0.53.1", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" @@ -3035,6 +3283,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + [[package]] name = "windows_aarch64_msvc" version = "0.52.6" @@ -3047,6 +3301,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -3071,6 +3331,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + [[package]] name = "windows_i686_msvc" version = "0.52.6" @@ -3083,6 +3349,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + [[package]] name = "windows_x86_64_gnu" version = "0.52.6" @@ -3095,6 +3367,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" @@ -3107,6 +3385,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + [[package]] name = "windows_x86_64_msvc" version = "0.52.6" @@ -3155,7 +3439,7 @@ dependencies = [ "heck", "indexmap", "prettyplease", - "syn 2.0.117", + "syn", "wasm-metadata", "wit-bindgen-core", "wit-component", @@ -3171,7 +3455,7 @@ dependencies = [ "prettyplease", "proc-macro2", "quote", - "syn 2.0.117", + "syn", "wit-bindgen-core", "wit-bindgen-rust", ] @@ -3256,7 +3540,7 @@ checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", "synstructure", ] @@ -3278,7 +3562,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -3298,7 +3582,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", "synstructure", ] @@ -3319,7 +3603,7 @@ checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -3341,7 +3625,7 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -3373,9 +3657,15 @@ dependencies = [ [[package]] name = "zlib-rs" -version = "0.4.2" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b20717f0917c908dc63de2e44e97f1e6b126ca58d0e391cee86d504eb8fbd05" +checksum = "c745c48e1007337ed136dc99df34128b9faa6ed542d80a1c673cf55a6d7236c8" + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" [[package]] name = "zopfli" diff --git a/Cargo.toml b/Cargo.toml index c796a67..6e2663f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,40 +10,40 @@ description = "Easy setup and management of Nextcloud test instances using docke [dependencies] bollard = "0.20.1" maplit = "1.0.2" -camino = { version = "1.1.7", features = ["serde1"] } -tokio = { version = "1.38.0", features = ["fs", "macros", "signal", "rt-multi-thread"] } -futures-util = "0.3.30" -termion = "4.0.1" -opener = "0.7.1" +camino = { version = "1.2.2", features = ["serde1"] } +tokio = { version = "1.49.0", features = ["fs", "macros", "signal", "rt-multi-thread"] } +futures-util = "0.3.32" +termion = "4.0.6" +opener = "0.8.4" toml = "1.0.3" directories-next = "2.0.0" -serde = "1.0.203" -serde_json = "1.0.117" +serde = "1.0.228" +serde_json = "1.0.149" petname = "2.0.2" -reqwest = { version = "0.12.4", default-features = false, features = ["rustls-tls-native-roots"] } -tar = "0.4.41" -flate2 = "1.0.30" -async-trait = "0.1.80" +reqwest = { version = "0.13.2", default-features = false, features = ["rustls"] } +tar = "0.4.44" +flate2 = "1.1.9" +async-trait = "0.1.89" enum_dispatch = "0.3.13" -miette = { version = "7.2.0", features = ["fancy"] } -shell-words = "1.1.0" -tracing = "0.1.40" -tracing-subscriber = "0.3.18" +miette = { version = "7.6.0", features = ["fancy"] } +shell-words = "1.1.1" +tracing = "0.1.44" +tracing-subscriber = "0.3.22" atty = "0.2.14" -git2 = { version = "0.20.0", default-features = false } +git2 = { version = "0.20.4", default-features = false } itertools = { version = "0.14.0", features = ["use_alloc"] } -local-ip-address = "0.6.5" -strum = { version = "0.27.2", features = ["derive"] } -owo-colors = { version = "4.2.2", features = ["supports-colors"] } +local-ip-address = "0.6.10" +strum = { version = "0.28.0", features = ["derive"] } +owo-colors = { version = "4.3.0", features = ["supports-colors"] } zip = "8.1.0" sha2 = "0.11.0-rc.5" base16ct = { version = "1.0.0", features = ["alloc"] } indicatif = "0.18.4" hyper-reverse-proxy = { version = "0.5.2-dev", git = "https://github.com/chpio/hyper-reverse-proxy", rev = "6934877eb74465204f605cc1c05ca5a9772db7c0" } -hyper = "1.6.0" -hyper-util = "0.1.10" -axum = { version = "0.8.1", features = ["tokio"] } +hyper = "1.8.1" +hyper-util = "0.1.20" +axum = { version = "0.8.8", features = ["tokio"] } [profile.release] lto = true From 37248955ab2d9c59b303a465a826c5b2724de0bd Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Fri, 27 Feb 2026 23:03:24 +0100 Subject: [PATCH 09/68] 2.1.6 --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1b97063..d73ab8d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -932,7 +932,7 @@ checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" [[package]] name = "haze" -version = "2.1.5" +version = "2.1.6" dependencies = [ "async-trait", "atty", diff --git a/Cargo.toml b/Cargo.toml index 6e2663f..5760bd8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "haze" -version = "2.1.5" +version = "2.1.6" authors = ["Robin Appelman "] edition = "2021" repository = "https://codeberg.org/icewind/haze" From 903b3d25a887e89e16b300e1af5fc2f98174b94f Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Mon, 2 Mar 2026 15:43:28 +0100 Subject: [PATCH 10/68] localhost proxy doesn't support federation --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index bda3823..e5ba037 100644 --- a/README.md +++ b/README.md @@ -276,8 +276,8 @@ proxy to allow using a wildcard domain. ### Setup - Set a DNS record for `*.haze.exmaple.com` and `haze.example.com` pointing to - `127.0.0.1` or your development machine if you need to access it from other - devices. + your development machine. Pointing it to `127.0.0.1` will also work, but comes + with limitations like federation not being supported. - Set the `proxy` configuration with your domain and desired listen endpoint. - Set up a service to run `haze proxy` in the background as your own user. A systemd user service is recommended (see [haze.service](./haze.service) for an From d89c547f2a5b595de60b4a47005b3a7ac7c1520f Mon Sep 17 00:00:00 2001 From: provokateurin Date: Thu, 5 Mar 2026 15:21:47 +0100 Subject: [PATCH 11/68] fix(install): Fix OCI Signed-off-by: provokateurin --- nix/image/scripts/install | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nix/image/scripts/install b/nix/image/scripts/install index d216118..2a86fcc 100755 --- a/nix/image/scripts/install +++ b/nix/image/scripts/install @@ -10,9 +10,9 @@ fi cd $WEBROOT -if [ "$SQL" = "oci" ]; then +if [ "$SQL" = "oracle" ]; then # oracle is a special snowflake - occ maintenance:install --admin-user=$USER --admin-pass=$PASSWORD --database=$SQL --database-name=xe --database-host=$SQL --database-user=system --database-pass=haze + occ maintenance:install --admin-user=$USER --admin-pass=$PASSWORD --database=oci --database-name=xe --database-host=$SQL --database-user=system --database-pass=haze elif [ "$SQL" = "mariadb" ]; then occ maintenance:install --admin-user=$USER --admin-pass=$PASSWORD --database=mysql --database-name=haze --database-host=$SQL --database-user=haze --database-pass=haze else From 862d33b0174f7c0be24a2dfe20655d05cfb92a81 Mon Sep 17 00:00:00 2001 From: provokateurin Date: Thu, 5 Mar 2026 15:32:11 +0100 Subject: [PATCH 12/68] feat(cloud): Kill containers before removing them for faster destruction Signed-off-by: provokateurin --- src/cloud.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/cloud.rs b/src/cloud.rs index 79d298c..d0b645c 100644 --- a/src/cloud.rs +++ b/src/cloud.rs @@ -499,6 +499,14 @@ impl Cloud { pub async fn destroy(self, docker: &Docker) -> Result<()> { for container in self.containers { + docker + .kill_container( + container.trim_start_matches('/'), + None, + ) + .await + .into_diagnostic() + .wrap_err("Failed to kill container")?; docker .remove_container( container.trim_start_matches('/'), From b7ea4e9760fc05d307bdff33e40efaec197cfa82 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Thu, 5 Mar 2026 23:27:54 +0100 Subject: [PATCH 13/68] allow configuring additional app directories and add a writable app directory fixes #15 --- README.md | 1 + nix/image/configs/nc/config.php | 12 +++++- src/args.rs | 1 + src/cloud.rs | 69 ++++++++++++++++++++++++--------- src/config.rs | 16 +++++++- src/mapping.rs | 65 +++++++++++++++++++++++++------ 6 files changed, 130 insertions(+), 34 deletions(-) diff --git a/README.md b/README.md index e5ba037..5eea607 100644 --- a/README.md +++ b/README.md @@ -303,6 +303,7 @@ options ```toml sources_root = "/path/to/sources" # path of the nextcloud sources. required +app_directories = ["/path/to/sources/more_app"] # paths to additional app directories. work_dir = "/path/to/temp/dir" # path to temporary directory. optional, defaults to "/tmp/haze" [auto_setup] # optional diff --git a/nix/image/configs/nc/config.php b/nix/image/configs/nc/config.php index e8dc0af..49a135f 100644 --- a/nix/image/configs/nc/config.php +++ b/nix/image/configs/nc/config.php @@ -1,4 +1,12 @@ - true, 'appstoreenabled' => false, 'memcache.local' => '\\OC\\Memcache\\APCu', @@ -9,4 +17,4 @@ 'profiling.secret' => 'haze', 'profiling.path' => '/tmp/profiling', //PLACEHOLDER -]; +]); diff --git a/src/args.rs b/src/args.rs index f5434ca..e4e2b69 100644 --- a/src/args.rs +++ b/src/args.rs @@ -505,6 +505,7 @@ impl SubCommand for HazeCommand { fn test_arg_parse() { let config = HazeConfig { sources_root: Default::default(), + app_directories: Default::default(), work_dir: Default::default(), auto_setup: Default::default(), volume: vec![], diff --git a/src/cloud.rs b/src/cloud.rs index d0b645c..c4de8de 100644 --- a/src/cloud.rs +++ b/src/cloud.rs @@ -1,7 +1,7 @@ use crate::config::{HazeConfig, HazeVolumeConfig}; use crate::database::Database; use crate::exec::{exec, exec_io, exec_tty, ExitCode}; -use crate::mapping::{default_mappings, Mapping}; +use crate::mapping::{for_config, Mapping}; use crate::php::{PhpVersion, PHP_MEMORY_LIMIT}; use crate::service::Service; use crate::service::ServiceTrait; @@ -15,14 +15,14 @@ use flate2::read::GzDecoder; use futures_util::future::try_join_all; use miette::{IntoDiagnostic, Report, Result, WrapErr}; use petname::petname; -use serde_json::Value; +use serde_json::{Map, Value}; use std::borrow::Cow; use std::collections::HashMap; use std::fmt::Display; use std::fs; -use std::fs::read_to_string; +use std::fs::{read_to_string, write}; use std::io::{stdout, Cursor, Read, Stdout, Write}; -use std::iter::Peekable; +use std::iter::{once, Peekable}; use std::net::IpAddr; use std::os::unix::fs::MetadataExt; use std::str::FromStr; @@ -286,11 +286,8 @@ impl Cloud { }) }) .collect::>>()?; - let mappings = config - .volume - .iter() - .map(Mapping::from) - .chain(default_mappings()) + + let mappings = for_config(config) .chain(app_volumes.iter().map(Mapping::from)) .collect::>(); for mapping in &mappings { @@ -300,6 +297,48 @@ impl Cloud { .wrap_err_with(|| format!("Failed to setup work directory {}", mapping.source))?; } + let mut nc_config = Value::Object(Map::new()); + nc_config["apps_paths"] = Value::Array( + once("apps") + .chain( + config + .app_directories + .iter() + .filter_map(|dir| dir.file_name()), + ) + .map(|name| { + [ + ( + String::from("path"), + Value::from(format!("/var/www/html/{}", name)), + ), + (String::from("url"), Value::from(format!("/{}", name))), + (String::from("writable"), Value::from(false)), + ] + .into_iter() + .collect() + }) + .chain(once( + [ + ( + String::from("path"), + Value::from("/var/www/html/store_apps"), + ), + (String::from("url"), Value::from("/store_apps")), + (String::from("writable"), Value::from(true)), + ] + .into_iter() + .collect(), + )) + .collect(), + ); + write( + workdir.join("config/nextcloud.json"), + serde_json::to_string_pretty(&nc_config).unwrap(), + ) + .into_diagnostic() + .wrap_err("Failed to write config json")?; + let network = docker .create_network(NetworkCreateRequest { name: id.clone(), @@ -500,10 +539,7 @@ impl Cloud { pub async fn destroy(self, docker: &Docker) -> Result<()> { for container in self.containers { docker - .kill_container( - container.trim_start_matches('/'), - None, - ) + .kill_container(container.trim_start_matches('/'), None) .await .into_diagnostic() .wrap_err("Failed to kill container")?; @@ -802,12 +838,7 @@ impl Cloud { format!("/var/www/html/{path}").into() }; - let mut mappings = config - .volume - .iter() - .map(Mapping::from) - .chain(default_mappings()) - .collect::>(); + let mut mappings = for_config(config).collect::>(); mappings.sort_by_key(|mapping| usize::MAX - mapping.target.as_str().len()); for mapping in mappings { diff --git a/src/config.rs b/src/config.rs index 07cfcef..fad1d0a 100644 --- a/src/config.rs +++ b/src/config.rs @@ -4,7 +4,7 @@ use miette::{IntoDiagnostic, Report, Result, WrapErr}; use serde::Deserialize; use std::collections::HashMap; use std::convert::TryFrom; -use std::env::var; +use std::env::home_dir; use std::fs::read_to_string; use std::net::IpAddr; use toml::Value; @@ -13,6 +13,7 @@ use toml::Value; #[serde(from = "RawHazeConfig")] pub struct HazeConfig { pub sources_root: Utf8PathBuf, + pub app_directories: Vec, pub work_dir: Utf8PathBuf, pub auto_setup: HazeAutoSetupConfig, pub volume: Vec, @@ -27,6 +28,8 @@ pub struct RawHazeConfig { #[serde(default = "default_work_dir")] pub work_dir: Utf8PathBuf, #[serde(default)] + pub app_directories: Vec, + #[serde(default)] pub auto_setup: HazeAutoSetupConfig, #[serde(default)] pub volume: Vec, @@ -42,7 +45,11 @@ impl From for HazeConfig { fn from(raw: RawHazeConfig) -> Self { fn normalize_path(path: Utf8PathBuf) -> Utf8PathBuf { if path.starts_with("~") { - let home = var("HOME").expect("HOME not set"); + let home = home_dir().expect("can't detect home directory"); + let home = home + .into_os_string() + .into_string() + .expect("non-utf8 home directory"); format!("{}{}", home, &path.as_str()[1..]).into() } else { path @@ -51,6 +58,11 @@ impl From for HazeConfig { HazeConfig { sources_root: normalize_path(raw.sources_root), + app_directories: raw + .app_directories + .into_iter() + .map(normalize_path) + .collect(), work_dir: normalize_path(raw.work_dir), auto_setup: raw.auto_setup, volume: raw.volume, diff --git a/src/mapping.rs b/src/mapping.rs index 4061100..36587ad 100644 --- a/src/mapping.rs +++ b/src/mapping.rs @@ -1,13 +1,14 @@ use crate::config::{HazeConfig, HazeVolumeConfig}; use camino::{Utf8Path, Utf8PathBuf}; use miette::{IntoDiagnostic, Result}; +use std::borrow::Cow; use tokio::fs::{create_dir_all, write}; #[derive(Debug)] pub struct Mapping<'a> { source_type: MappingSourceType, - pub source: &'a Utf8Path, - pub target: &'a Utf8Path, + pub source: Cow<'a, Utf8Path>, + pub target: Cow<'a, Utf8Path>, mapping_type: MappingType, read_only: bool, map: bool, @@ -23,6 +24,26 @@ impl<'a> Mapping<'a> { where Target: Into<&'a Utf8Path>, Source: Into<&'a Utf8Path>, + { + Mapping { + source_type, + source: Cow::Borrowed(source.into()), + target: Cow::Borrowed(target.into()), + mapping_type: MappingType::Folder, + read_only: false, + map: true, + create: true, + } + } + + pub fn owned( + source_type: MappingSourceType, + source: Source, + target: Target, + ) -> Self + where + Target: Into>, + Source: Into>, { Mapping { source_type, @@ -65,10 +86,10 @@ impl<'a> Mapping<'a> { return Ok(()); } let source = match self.source_type { - MappingSourceType::WorkDir => config.work_dir.join(id).join(self.source), - MappingSourceType::GlobalWorkDir => config.work_dir.join(self.source), + MappingSourceType::WorkDir => config.work_dir.join(id).join(self.source.as_ref()), + MappingSourceType::GlobalWorkDir => config.work_dir.join(self.source.as_ref()), MappingSourceType::Sources => return Ok(()), - MappingSourceType::Absolute => self.source.into(), + MappingSourceType::Absolute => self.source.as_ref().into(), }; match self.mapping_type { MappingType::Folder => create_dir_all(source).await.into_diagnostic()?, @@ -80,10 +101,10 @@ impl<'a> Mapping<'a> { pub fn source(&self, id: &str, config: &HazeConfig, source_root: &Utf8Path) -> Utf8PathBuf { match self.source_type { - MappingSourceType::WorkDir => config.work_dir.join(id).join(self.source), - MappingSourceType::GlobalWorkDir => config.work_dir.join(self.source), - MappingSourceType::Sources => source_root.join(self.source), - MappingSourceType::Absolute => self.source.into(), + MappingSourceType::WorkDir => config.work_dir.join(id).join(self.source.as_ref()), + MappingSourceType::GlobalWorkDir => config.work_dir.join(self.source.as_ref()), + MappingSourceType::Sources => source_root.join(self.source.as_ref()), + MappingSourceType::Absolute => self.source.as_ref().into(), } } @@ -112,6 +133,7 @@ pub fn default_mappings<'a>() -> impl IntoIterator> { Mapping::new(Sources, "", "/var/www/html"), Mapping::new(WorkDir, "data", "/var/www/html/data"), Mapping::new(WorkDir, "config", "/var/www/html/config"), + Mapping::new(WorkDir, "store_app", "/var/www/html/store_app"), Mapping::new(WorkDir, "data-autotest", "/var/www/html/data-autotest"), Mapping::new(WorkDir, "skeleton", "/var/www/html/core/skeleton"), Mapping::new( @@ -168,9 +190,30 @@ pub fn default_mappings<'a>() -> impl IntoIterator> { Mapping::new(WorkDir, "profiling", "/tmp/profiling"), Mapping::new(WorkDir, "php-config", "/config"), ]; + IntoIterator::into_iter(mappings) } +pub fn for_config<'a>(config: &'a HazeConfig) -> impl Iterator> { + let app_dir_mappings = config.app_directories.iter().map(|dir| { + Mapping::owned( + MappingSourceType::Absolute, + dir.as_path(), + Cow::Owned(Utf8PathBuf::from(format!( + "/var/www/html/{}", + dir.file_name().unwrap() + ))), + ) + }); + + config + .volume + .iter() + .map(Mapping::from) + .chain(app_dir_mappings) + .chain(default_mappings()) +} + #[derive(Debug, Copy, Clone)] pub enum MappingSourceType { Sources, @@ -194,8 +237,8 @@ impl<'a> From<&'a HazeVolumeConfig> for Mapping<'a> { }; Mapping { source_type: MappingSourceType::Absolute, - source: config.source.as_path(), - target: config.target.as_path(), + source: Cow::Borrowed(config.source.as_path()), + target: Cow::Borrowed(config.target.as_path()), mapping_type: ty, read_only: config.read_only, map: true, From 9629dea8dfa2d6943e23d1d5d28113690fa1c36c Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Thu, 5 Mar 2026 23:36:47 +0100 Subject: [PATCH 14/68] enable appstore --- nix/image/configs/nc/config.php | 1 - src/mapping.rs | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/nix/image/configs/nc/config.php b/nix/image/configs/nc/config.php index 49a135f..b00d47b 100644 --- a/nix/image/configs/nc/config.php +++ b/nix/image/configs/nc/config.php @@ -8,7 +8,6 @@ if (file_exists(__DIR__ . '/nextcloud.json')) { $CONFIG = array_merge_recursive($extra_config, [ 'debug' => true, - 'appstoreenabled' => false, 'memcache.local' => '\\OC\\Memcache\\APCu', 'memcache.distributed' => '\\OC\\Memcache\\APCu', 'memcache.locking' => '\\OC\\Memcache\\APCu', diff --git a/src/mapping.rs b/src/mapping.rs index 36587ad..1e04ffb 100644 --- a/src/mapping.rs +++ b/src/mapping.rs @@ -133,7 +133,7 @@ pub fn default_mappings<'a>() -> impl IntoIterator> { Mapping::new(Sources, "", "/var/www/html"), Mapping::new(WorkDir, "data", "/var/www/html/data"), Mapping::new(WorkDir, "config", "/var/www/html/config"), - Mapping::new(WorkDir, "store_app", "/var/www/html/store_app"), + Mapping::new(WorkDir, "store_apps", "/var/www/html/store_apps"), Mapping::new(WorkDir, "data-autotest", "/var/www/html/data-autotest"), Mapping::new(WorkDir, "skeleton", "/var/www/html/core/skeleton"), Mapping::new( From 1a6dd904101cbff2d6848cfd90fb8d76c7db3859 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Fri, 6 Mar 2026 00:15:35 +0100 Subject: [PATCH 15/68] add appDirectories to hm module --- nix/hm-module.nix | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/nix/hm-module.nix b/nix/hm-module.nix index 096d853..072fda4 100644 --- a/nix/hm-module.nix +++ b/nix/hm-module.nix @@ -9,6 +9,7 @@ with lib; let format = pkgs.formats.toml {}; configFile = format.generate "haze.toml" ({ sources_root = cfg.sourcesRoot; + app_directories = cfg.appDirectories; work_dir = cfg.workDir; auto_setup = { enabled = cfg.autoSetup.enable; @@ -61,6 +62,12 @@ in { description = "Directory to store instance data"; }; + appDirectories = mkOption { + type = types.listOf types.str; + default = []; + description = "Paths to additional app directories"; + }; + autoSetup = mkOption { type = types.submodule { options = { From e9cb4f08e3e7ea709441638a9682392598367e19 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Fri, 6 Mar 2026 00:15:35 +0100 Subject: [PATCH 16/68] use extra app directories for git operations --- src/args.rs | 17 +++++++++++++---- src/git.rs | 39 +++++++++++++++++++++++++-------------- src/main.rs | 7 ++++--- 3 files changed, 42 insertions(+), 21 deletions(-) diff --git a/src/args.rs b/src/args.rs index e4e2b69..9b7a2cc 100644 --- a/src/args.rs +++ b/src/args.rs @@ -124,9 +124,12 @@ pub enum GitOperation { /// /// "main" and "master" can be used interchangeably. #[strum(props( - Args = "[branch] branch to checkout, defaults to the branch matching the current server version" + Args = "[branch] branch to checkout, defaults to the branch matching the current server version [-v] verbose" ))] - Checkout { branch: Option }, + Checkout { + branch: Option, + verbose: bool, + }, } impl SubCommand for GitOperation { @@ -320,18 +323,24 @@ impl HazeArgs { HazeCommand::Checkout => { let branch = args.next().map(S::into); Ok(HazeArgs::Git { - operation: GitOperation::Checkout { branch }, + operation: GitOperation::Checkout { + branch, + verbose: false, + }, }) } HazeCommand::Git => { + let mut args = args.peekable(); let operation = args .next() .ok_or_else(|| Report::msg("No git operation provided"))?; match operation.as_ref() { "checkout" => { + let verbose = args.next_if(|arg| arg.as_ref() == "-v").is_some(); let branch = args.next().map(S::into); + let verbose = verbose | args.next_if(|arg| arg.as_ref() == "-v").is_some(); Ok(HazeArgs::Git { - operation: GitOperation::Checkout { branch }, + operation: GitOperation::Checkout { branch, verbose }, }) } "pull" => Ok(HazeArgs::Git { diff --git a/src/git.rs b/src/git.rs index d08f2f3..3a797c3 100644 --- a/src/git.rs +++ b/src/git.rs @@ -1,22 +1,29 @@ +use crate::config::HazeConfig; use crate::Result; use git2::build::CheckoutBuilder; use git2::{Branch, BranchType, Repository, RepositoryState}; use miette::{Context, IntoDiagnostic}; use std::fs::read_dir; -use std::path::{Path, PathBuf}; +use std::iter::once; +use std::path::PathBuf; use std::process::Command; -fn find_app_repos(root: impl AsRef) -> Result> { - let apps_dir = root.as_ref().join("apps"); - Ok(read_dir(apps_dir) - .into_diagnostic()? +fn find_app_repos(config: &HazeConfig) -> Result> { + let apps_dirs = once(config.sources_root.as_path().join("apps")) + .chain(config.app_directories.iter().cloned()); + let dir_handles = apps_dirs + .map(|dir| read_dir(dir).into_diagnostic()) + .collect::>>()?; + Ok(dir_handles + .into_iter() + .flatten() .flatten() .filter(|app| app.path().join(".git").is_dir()) .map(|app| app.path())) } -fn longest_app_branch(root: impl AsRef) -> Result<(usize, usize)> { - Ok(find_app_repos(root)? +fn longest_app_branch(config: &HazeConfig) -> Result<(usize, usize)> { + Ok(find_app_repos(config)? .filter_map(|app_dir| { let app_name = app_dir.file_name()?.to_str()?; let repo = Repository::init(&app_dir).ok()?; @@ -27,27 +34,32 @@ fn longest_app_branch(root: impl AsRef) -> Result<(usize, usize)> { .unwrap_or_default()) } -pub fn checkout_all>(sources_root: P, mut name: &str) -> Result<()> { +pub fn checkout_all(config: &HazeConfig, mut name: &str, verbose: bool) -> Result<()> { // "main" and "master" are interchangeable if name == "main" { name = "master"; } - for app_dir in find_app_repos(sources_root)? { + for app_dir in find_app_repos(config)? { let repo = Repository::init(&app_dir) .into_diagnostic() .wrap_err_with(|| format!("Failed to open repository {}", app_dir.display()))?; + let app_name = app_dir.file_name().unwrap().to_string_lossy(); if let Some(branch) = get_branch(&repo, name)? { if !branch.is_head() { let is_remote = branch.get().is_remote(); - print!("{}", app_dir.file_name().unwrap().to_string_lossy()); + print!("{app_name}"); if let Err(e) = checkout(&repo, &branch, is_remote.then_some(name)) { println!(": {:#} ❌", e); } else { println!(" ✓"); } + } else if verbose { + println!("{app_name} -"); } + } else if verbose { + println!("{app_name} 🛇 Branch not found"); }; } Ok(()) @@ -68,11 +80,10 @@ const GIT_BINARY: &str = match option_env!("GIT_BINARY") { None => "git", }; -pub fn pull_all>(sources_root: P) -> Result<()> { - let sources_root = sources_root.as_ref(); - let (max_app, max_branch) = longest_app_branch(sources_root)?; +pub fn pull_all(config: &HazeConfig) -> Result<()> { + let (max_app, max_branch) = longest_app_branch(config)?; - for app_dir in find_app_repos(sources_root)? { + for app_dir in find_app_repos(config)? { let app_name = app_dir.file_name().unwrap().to_string_lossy(); let repo = Repository::init(&app_dir) .into_diagnostic() diff --git a/src/main.rs b/src/main.rs index 5deb172..5eb6e0c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -373,15 +373,16 @@ async fn main() -> Result { proxy(docker, config).await?; } HazeArgs::Git { operation } => match operation { - GitOperation::Checkout { branch } => { + GitOperation::Checkout { branch, verbose } => { let sources = Sources::new(&config.sources_root)?; checkout_all( - &config.sources_root, + &config, &branch.unwrap_or_else(|| sources.get_server_version_branch()), + verbose, )?; } GitOperation::Pull => { - pull_all(&config.sources_root)?; + pull_all(&config)?; } }, HazeArgs::Env { From bf7a8d9a3445da462a184297726944ef8e2dc748 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Fri, 6 Mar 2026 00:48:12 +0100 Subject: [PATCH 17/68] dont polute store_app --- nix/image/nginx.conf | 10 ++++++++++ src/cloud.rs | 5 +---- src/mapping.rs | 2 +- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/nix/image/nginx.conf b/nix/image/nginx.conf index 3de81ca..6e707c2 100644 --- a/nix/image/nginx.conf +++ b/nix/image/nginx.conf @@ -69,6 +69,16 @@ http { access_log off; } + location ^~ /store_apps { + root /var/www; + try_files $uri /index.php$request_uri; + access_log off; # Optional: Don't log access to assets + + location ~ \.wasm$ { + default_type application/wasm; + } + } + include /conf/nginx-app.conf; location ~ \.php(?:$|/) { diff --git a/src/cloud.rs b/src/cloud.rs index c4de8de..7232e33 100644 --- a/src/cloud.rs +++ b/src/cloud.rs @@ -320,10 +320,7 @@ impl Cloud { }) .chain(once( [ - ( - String::from("path"), - Value::from("/var/www/html/store_apps"), - ), + (String::from("path"), Value::from("/var/www/store_apps")), (String::from("url"), Value::from("/store_apps")), (String::from("writable"), Value::from(true)), ] diff --git a/src/mapping.rs b/src/mapping.rs index 1e04ffb..9c01da0 100644 --- a/src/mapping.rs +++ b/src/mapping.rs @@ -133,7 +133,7 @@ pub fn default_mappings<'a>() -> impl IntoIterator> { Mapping::new(Sources, "", "/var/www/html"), Mapping::new(WorkDir, "data", "/var/www/html/data"), Mapping::new(WorkDir, "config", "/var/www/html/config"), - Mapping::new(WorkDir, "store_apps", "/var/www/html/store_apps"), + Mapping::new(WorkDir, "store_apps", "/var/www/store_apps"), Mapping::new(WorkDir, "data-autotest", "/var/www/html/data-autotest"), Mapping::new(WorkDir, "skeleton", "/var/www/html/core/skeleton"), Mapping::new( From 8e79e997a89f19b5caf8eae2ab7c3ec9f1f73294 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Mon, 9 Mar 2026 17:16:24 +0100 Subject: [PATCH 18/68] make proxy work for object store --- src/service/objectstore.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/service/objectstore.rs b/src/service/objectstore.rs index 2225dca..4f0b7d1 100644 --- a/src/service/objectstore.rs +++ b/src/service/objectstore.rs @@ -187,4 +187,11 @@ impl ServiceTrait for ObjectStore { Ok(Vec::new()) } } + + fn proxy_port(&self) -> u16 { + match self { + ObjectStore::S3 | ObjectStore::S3m | ObjectStore::S3mb => 9000, + ObjectStore::Azure => 10000, + } + } } From fdc821cb93f3f06b4fd53cc34474310873ab005d Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Mon, 9 Mar 2026 17:35:47 +0100 Subject: [PATCH 19/68] allow bare-service proxy hosts --- src/cloud.rs | 8 +++++- src/proxy.rs | 74 ++++++++++++++++++++++++++++++++++++---------------- 2 files changed, 58 insertions(+), 24 deletions(-) diff --git a/src/cloud.rs b/src/cloud.rs index 7232e33..a1c39c9 100644 --- a/src/cloud.rs +++ b/src/cloud.rs @@ -218,7 +218,7 @@ fn test_option_parse() { ); } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Cloud { pub id: String, pub network: String, @@ -851,3 +851,9 @@ impl Cloud { None } } + +impl PartialEq for Cloud { + fn eq(&self, other: &Self) -> bool { + self.id == other.id + } +} diff --git a/src/proxy.rs b/src/proxy.rs index ee867c9..c99c216 100644 --- a/src/proxy.rs +++ b/src/proxy.rs @@ -1,4 +1,4 @@ -use crate::service::ServiceTrait; +use crate::service::{ServiceTrait, ServiceType}; use crate::Result; use crate::{Cloud, HazeConfig}; use axum::http::header::HOST; @@ -18,6 +18,7 @@ use std::fs::{create_dir_all, set_permissions}; use std::net::{IpAddr, Ipv4Addr, SocketAddr}; use std::os::unix::fs::PermissionsExt; use std::path::PathBuf; +use std::str::FromStr; use std::sync::{Arc, Mutex}; use std::time::Duration; use tokio::net::UnixListener; @@ -28,7 +29,7 @@ use tracing::{debug, error, info}; struct ActiveInstances { known: Mutex>, - last: Mutex>, + last: Mutex>, docker: Docker, config: HazeConfig, } @@ -48,15 +49,9 @@ impl ActiveInstances { return Some(ip); } - // service proxy - let addr = if name.matches('-').count() == 2 { - let (name, service_name) = name.rsplit_once('-').unwrap(); - let cloud = Cloud::get_by_filter(&self.docker, Some(name.into()), &self.config) - .await - .ok()?; - let service = cloud - .services() - .find(|service| service.name() == service_name)?; + let addr = if ServiceType::from_str(name).is_ok() { + let cloud = self.last()?; + let service = cloud.services().find(|service| service.name() == name)?; let ip = service .get_ips(&self.docker, &cloud.id) .await @@ -64,13 +59,33 @@ impl ActiveInstances { .next()?; SocketAddr::new(ip, service.proxy_port()) } else { - SocketAddr::new( - Cloud::get_by_filter(&self.docker, Some(name.into()), &self.config) - .await - .ok()? - .ip?, - 80, - ) + match name.matches('-').count() { + // instance + 1 => SocketAddr::new( + Cloud::get_by_filter(&self.docker, Some(name.into()), &self.config) + .await + .ok()? + .ip?, + 80, + ), + // service with instance + 2 => { + let (name, service_name) = name.rsplit_once('-').unwrap(); + let cloud = Cloud::get_by_filter(&self.docker, Some(name.into()), &self.config) + .await + .ok()?; + let service = cloud + .services() + .find(|service| service.name() == service_name)?; + let ip = service + .get_ips(&self.docker, &cloud.id) + .await + .ok()? + .next()?; + SocketAddr::new(ip, service.proxy_port()) + } + _ => return None, + } }; println!("{name} => {addr}"); @@ -79,18 +94,31 @@ impl ActiveInstances { Some(addr) } - pub fn last(&self) -> Option { - *self.last.lock().unwrap() + pub fn last_addr(&self) -> Option { + self.last + .lock() + .unwrap() + .as_ref() + .and_then(|cloud| Some(SocketAddr::new(cloud.ip?, 80))) + } + + pub fn last(&self) -> Option { + self.last.lock().unwrap().clone() } async fn update_last(&self) { let last = Cloud::get_by_filter(&self.docker, None, &self.config) .await - .ok() - .and_then(|cloud| Some(SocketAddr::new(cloud.ip?, 80))); + .ok(); let mut old = self.last.lock().unwrap(); if old.as_ref() != last.as_ref() { info!(instance = ?last, "Found new instance"); + + // remove cached base-service mappings + self.known + .lock() + .unwrap() + .retain(|key, _| ServiceType::from_str(key).is_err()); *old = last; } } @@ -181,7 +209,7 @@ async fn get_remote( }; let ip = if host == base_address { instances - .last() + .last_addr() .ok_or_else(|| String::from("No running instance known")) } else { let requested_instance = host.split('.').next().unwrap(); From 85071d7aa1555026658aeb456d0544f889b4a0e4 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Mon, 9 Mar 2026 17:48:57 +0100 Subject: [PATCH 20/68] allow service names with - in them in proxy --- src/proxy.rs | 5 +++-- src/service.rs | 5 +++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/proxy.rs b/src/proxy.rs index c99c216..d5a3b0e 100644 --- a/src/proxy.rs +++ b/src/proxy.rs @@ -69,8 +69,9 @@ impl ActiveInstances { 80, ), // service with instance - 2 => { - let (name, service_name) = name.rsplit_once('-').unwrap(); + 2.. => { + let service_name = name.splitn(3, '-').last()?; + let name = &name[0..(name.len() - service_name.len() - 1)]; let cloud = Cloud::get_by_filter(&self.docker, Some(name.into()), &self.config) .await .ok()?; diff --git a/src/service.rs b/src/service.rs index 8874352..77ea246 100644 --- a/src/service.rs +++ b/src/service.rs @@ -208,6 +208,8 @@ pub enum ServiceType { Azure, /// Ldap user backend Ldap, + /// Ldap admin interface + LdapAdmin, /// OnlyOffice OnlyOffice, /// Libre office online @@ -304,6 +306,9 @@ impl Service { ServiceType::S3mb => Some(vec![Service::ObjectStore(ObjectStore::S3mb)]), ServiceType::Azure => Some(vec![Service::ObjectStore(ObjectStore::Azure)]), ServiceType::Ldap => Some(vec![Service::Ldap(Ldap), Service::LdapAdmin(LdapAdmin)]), + ServiceType::LdapAdmin => { + Some(vec![Service::Ldap(Ldap), Service::LdapAdmin(LdapAdmin)]) + } ServiceType::OnlyOffice => Some(vec![Service::OnlyOffice(OnlyOffice)]), ServiceType::Office => Some(vec![Service::Office(Office)]), ServiceType::Push => Some(vec![Service::Push(NotifyPush)]), From 04b8ec975dcca5b99553b020d11a6165d02c24fe Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Mon, 9 Mar 2026 17:56:14 +0100 Subject: [PATCH 21/68] fix ldap admin --- src/service/ldap.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/service/ldap.rs b/src/service/ldap.rs index 7526918..66d1d14 100644 --- a/src/service/ldap.rs +++ b/src/service/ldap.rs @@ -115,7 +115,10 @@ impl ServiceTrait for LdapAdmin { }); let config = ContainerCreateBody { image: Some(image.into()), - env: Some(vec!["PHPLDAPADMIN_LDAP_HOSTS=ldap".into()]), + env: Some(vec![ + "PHPLDAPADMIN_LDAP_HOSTS=ldap".into(), + "PHPLDAPADMIN_HTTPS=false".into(), + ]), host_config: Some(HostConfig { network_mode: Some(network.to_string()), ..Default::default() From e76678ec14287c949f28d31a42a740b369583eb8 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Mon, 9 Mar 2026 17:56:14 +0100 Subject: [PATCH 22/68] document service proxy --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 5eea607..3bb98ca 100644 --- a/README.md +++ b/README.md @@ -296,6 +296,12 @@ subdomain of the configured domain, e.g. the `rolling-bees` instance will be available at `rolling-bees.haze.example.com`. Additionally, `haze.example.com` will automatically point to the last created instance. +Additionally, the proxy allows access to the server containers trough either +`-.haze.example.com` for a specific instance, or +`.haze.example.com` for the last created instead. For example +`rolling-bees-mail.haze.example.com` will give access to the smtp4dev web +interface of the `rolling-bees` instance. + ## Configuration Configuration is loaded from `~/.config/haze/haze.toml` and has the following From 88a41003401bb93af3c453efef0ae3e6e678851c Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Mon, 9 Mar 2026 18:08:34 +0100 Subject: [PATCH 23/68] show proxy addr for ldap-admin in startup message --- src/cloud.rs | 6 +++++- src/config.rs | 2 +- src/service.rs | 9 +++++++-- src/service/ldap.rs | 18 +++++++++++++----- 4 files changed, 26 insertions(+), 9 deletions(-) diff --git a/src/cloud.rs b/src/cloud.rs index a1c39c9..8437d63 100644 --- a/src/cloud.rs +++ b/src/cloud.rs @@ -493,6 +493,7 @@ impl Cloud { containers.push(container); let options_clone = options.clone(); + let proxy_config = config.proxy.clone(); let cloud_id = id.clone(); let docker_clone = docker.clone(); spawn(async move { @@ -506,7 +507,10 @@ impl Cloud { return; } for service in options_clone.services { - match service.start_message(&docker_clone, &cloud_id).await { + match service + .start_message(&docker_clone, &cloud_id, &proxy_config) + .await + { Ok(Some(msg)) => { println!("{}", msg); } diff --git a/src/config.rs b/src/config.rs index fad1d0a..b790b52 100644 --- a/src/config.rs +++ b/src/config.rs @@ -181,7 +181,7 @@ fn load_secret(name: &str, path: Option, raw: Option) -> Result< } } -#[derive(Default, Deserialize, Debug)] +#[derive(Default, Deserialize, Debug, Clone)] pub struct ProxyConfig { pub listen: String, #[serde(default)] diff --git a/src/service.rs b/src/service.rs index 77ea246..344ec49 100644 --- a/src/service.rs +++ b/src/service.rs @@ -16,7 +16,7 @@ mod sharded; mod smb; use crate::cloud::CloudOptions; -use crate::config::{HazeConfig, Preset}; +use crate::config::{HazeConfig, Preset, ProxyConfig}; pub use crate::service::clam::{Clam, ClamIcap, ClamIcapTls, ClamSocket}; use crate::service::dav::Dav; use crate::service::imaginary::Imaginary; @@ -78,7 +78,12 @@ pub trait ServiceTrait { None } - async fn start_message(&self, _docker: &Docker, _cloud_id: &str) -> Result> { + async fn start_message( + &self, + _docker: &Docker, + _cloud_id: &str, + _proxy: &ProxyConfig, + ) -> Result> { Ok(None) } diff --git a/src/service/ldap.rs b/src/service/ldap.rs index 66d1d14..aa082b0 100644 --- a/src/service/ldap.rs +++ b/src/service/ldap.rs @@ -1,5 +1,5 @@ use crate::cloud::CloudOptions; -use crate::config::HazeConfig; +use crate::config::{HazeConfig, ProxyConfig}; use crate::image::pull_image; use crate::service::ServiceTrait; use crate::Result; @@ -9,6 +9,8 @@ use bollard::query_parameters::CreateContainerOptions; use bollard::Docker; use maplit::hashmap; use miette::{IntoDiagnostic, Report}; +use std::net::IpAddr; +use std::str::FromStr; #[derive(Debug, Clone, Eq, PartialEq)] pub struct Ldap; @@ -151,9 +153,15 @@ impl ServiceTrait for LdapAdmin { Some(format!("{}-ldap-admin", cloud_id)) } - async fn start_message(&self, docker: &Docker, cloud_id: &str) -> Result> { + async fn start_message( + &self, + docker: &Docker, + cloud_id: &str, + proxy: &ProxyConfig, + ) -> Result> { + let id = self.container_name(cloud_id).unwrap(); let info = docker - .inspect_container(&self.container_name(cloud_id).unwrap(), None) + .inspect_container(&id, None) .await .into_diagnostic()?; let ip = if matches!( @@ -176,9 +184,9 @@ impl ServiceTrait for LdapAdmin { } else { return Err(Report::msg("ldap admin not started")); }; + let addr = proxy.addr(&id, IpAddr::from_str(&ip).unwrap()); Ok(Some(format!( - "Ldap admin running at: https://{} with 'cn=admin,dc=example,dc=org' and password 'haze'", - ip + "Ldap admin running at: {addr} with 'cn=admin,dc=example,dc=org' and password 'haze'" ))) } } From 80d71bd7a0107a3b3e84007d9f8de4b6f5f33a65 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Mon, 9 Mar 2026 18:30:32 +0100 Subject: [PATCH 24/68] move redis certificates --- {redis-certificates => certificates/redis}/ca.crt | 0 {redis-certificates => certificates/redis}/ca.key | 0 {redis-certificates => certificates/redis}/ca.txt | 0 .../redis}/client.crt | 0 .../redis}/client.key | 0 .../redis}/openssl.cnf | 0 .../redis}/redis.crt | 0 .../redis}/redis.dh | 0 .../redis}/redis.key | 0 .../redis}/server.crt | 0 .../redis}/server.key | 0 flake.nix | 2 +- nix/image/bootstrap.sh | 6 +++--- nix/image/configs/nc/redis-tls.php | 6 +++--- nix/image/haze.nix | 6 +++--- nix/package.nix | 2 +- src/main.rs | 14 +++++++------- 17 files changed, 18 insertions(+), 18 deletions(-) rename {redis-certificates => certificates/redis}/ca.crt (100%) rename {redis-certificates => certificates/redis}/ca.key (100%) rename {redis-certificates => certificates/redis}/ca.txt (100%) rename {redis-certificates => certificates/redis}/client.crt (100%) rename {redis-certificates => certificates/redis}/client.key (100%) rename {redis-certificates => certificates/redis}/openssl.cnf (100%) rename {redis-certificates => certificates/redis}/redis.crt (100%) rename {redis-certificates => certificates/redis}/redis.dh (100%) rename {redis-certificates => certificates/redis}/redis.key (100%) rename {redis-certificates => certificates/redis}/server.crt (100%) rename {redis-certificates => certificates/redis}/server.key (100%) diff --git a/redis-certificates/ca.crt b/certificates/redis/ca.crt similarity index 100% rename from redis-certificates/ca.crt rename to certificates/redis/ca.crt diff --git a/redis-certificates/ca.key b/certificates/redis/ca.key similarity index 100% rename from redis-certificates/ca.key rename to certificates/redis/ca.key diff --git a/redis-certificates/ca.txt b/certificates/redis/ca.txt similarity index 100% rename from redis-certificates/ca.txt rename to certificates/redis/ca.txt diff --git a/redis-certificates/client.crt b/certificates/redis/client.crt similarity index 100% rename from redis-certificates/client.crt rename to certificates/redis/client.crt diff --git a/redis-certificates/client.key b/certificates/redis/client.key similarity index 100% rename from redis-certificates/client.key rename to certificates/redis/client.key diff --git a/redis-certificates/openssl.cnf b/certificates/redis/openssl.cnf similarity index 100% rename from redis-certificates/openssl.cnf rename to certificates/redis/openssl.cnf diff --git a/redis-certificates/redis.crt b/certificates/redis/redis.crt similarity index 100% rename from redis-certificates/redis.crt rename to certificates/redis/redis.crt diff --git a/redis-certificates/redis.dh b/certificates/redis/redis.dh similarity index 100% rename from redis-certificates/redis.dh rename to certificates/redis/redis.dh diff --git a/redis-certificates/redis.key b/certificates/redis/redis.key similarity index 100% rename from redis-certificates/redis.key rename to certificates/redis/redis.key diff --git a/redis-certificates/server.crt b/certificates/redis/server.crt similarity index 100% rename from redis-certificates/server.crt rename to certificates/redis/server.crt diff --git a/redis-certificates/server.key b/certificates/redis/server.key similarity index 100% rename from redis-certificates/server.key rename to certificates/redis/server.key diff --git a/flake.nix b/flake.nix index 641c5fc..4d2f05b 100644 --- a/flake.nix +++ b/flake.nix @@ -24,7 +24,7 @@ }; extraPaths = [ - ./redis-certificates + ./certificates ]; withOverlays = [ diff --git a/nix/image/bootstrap.sh b/nix/image/bootstrap.sh index 5f85777..50c14c2 100755 --- a/nix/image/bootstrap.sh +++ b/nix/image/bootstrap.sh @@ -59,9 +59,9 @@ if [ -n "${REDIS_TLS:-}" ] then redis-server --protected-mode no \ --tls-port 6379 --port 0 \ - --tls-cert-file /redis-certificates/server.crt \ - --tls-key-file /redis-certificates/server.key \ - --tls-ca-cert-file /redis-certificates/ca.crt & + --tls-cert-file /certificates/redis/server.crt \ + --tls-key-file /certificates/redis/server.key \ + --tls-ca-cert-file /certificates/redis/ca.crt & else redis-server --protected-mode no & fi diff --git a/nix/image/configs/nc/redis-tls.php b/nix/image/configs/nc/redis-tls.php index fecc760..cb454ac 100644 --- a/nix/image/configs/nc/redis-tls.php +++ b/nix/image/configs/nc/redis-tls.php @@ -2,9 +2,9 @@ 'host' => 'tls://127.0.0.1', 'port' => 6379, 'ssl_context' => [ - 'local_cert' => '/redis-certificates/client.crt', - 'local_pk' => '/redis-certificates/client.key', - 'cafile' => '/redis-certificates/ca.crt', + 'local_cert' => '/certificates/redis/client.crt', + 'local_pk' => '/certificates/redis/client.key', + 'cafile' => '/certificates/redis/ca.crt', 'verify_peer_name' => false, ], ], diff --git a/nix/image/haze.nix b/nix/image/haze.nix index 38bc8f9..926f0fa 100644 --- a/nix/image/haze.nix +++ b/nix/image/haze.nix @@ -104,9 +104,9 @@ ''; }; - redis-certificates = runCommand "scripts" {} '' + certificates = runCommand "scripts" {} '' mkdir -p $out - cp -r ${../../redis-certificates} $out/redis-certificates + cp -r ${../../certificates} $out/certificates ''; clamav-data = runCommand "scripts" {} '' mkdir -p $out/etc @@ -170,7 +170,7 @@ in bootstrap configs scripts - redis-certificates + certificates clamav-data shadowSetupScript ]; diff --git a/nix/package.nix b/nix/package.nix index d79a216..fcd6489 100644 --- a/nix/package.nix +++ b/nix/package.nix @@ -7,7 +7,7 @@ inherit (lib) getExe; inherit (lib.sources) sourceByRegex; inherit (builtins) fromTOML readFile; - src = sourceByRegex ../. ["Cargo.*" "(src|redis-certificates)(/.*)?"]; + src = sourceByRegex ../. ["Cargo.*" "(src|certificates)(/.*)?"]; version = (fromTOML (readFile ../Cargo.toml)).package.version; in rustPlatform.buildRustPackage rec { diff --git a/src/main.rs b/src/main.rs index 5eb6e0c..2e4de9b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -421,16 +421,16 @@ async fn main() -> Result { ); if cloud.services().contains(&Service::RedisTls(RedisTls)) { - create_dir_all(config.work_dir.join("redis_certificates")) + create_dir_all(config.work_dir.join("certificates/redis")) .into_diagnostic() .wrap_err("Failed to create redis certificate directory")?; - let redis_cert_path = config.work_dir.join("redis_certificates/client.cert"); - let redis_key_path = config.work_dir.join("redis_certificates/client.key"); - let redis_ca_path = config.work_dir.join("redis_certificates/ca.cert"); + let redis_cert_path = config.work_dir.join("certificates/redis/client.cert"); + let redis_key_path = config.work_dir.join("certificates/redis/client.key"); + let redis_ca_path = config.work_dir.join("certificates/redis/ca.cert"); if !redis_cert_path.exists() { write( &redis_cert_path, - include_bytes!("../redis-certificates/client.crt"), + include_bytes!("../certificates/redis/client.crt"), ) .into_diagnostic() .wrap_err("Failed to write redis client certificate")?; @@ -438,7 +438,7 @@ async fn main() -> Result { if !redis_key_path.exists() { write( &redis_key_path, - include_bytes!("../redis-certificates/client.key"), + include_bytes!("../certificates/redis/client.key"), ) .into_diagnostic() .wrap_err("Failed to write redis client key")?; @@ -446,7 +446,7 @@ async fn main() -> Result { if !redis_ca_path.exists() { write( &redis_ca_path, - include_bytes!("../redis-certificates/ca.crt"), + include_bytes!("../certificates/redis/ca.crt"), ) .into_diagnostic() .wrap_err("Failed to write redis ca certificate")?; From b3a1e80f6f967c1c77f1be47cb0a0ae438e912ab Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Mon, 9 Mar 2026 18:42:43 +0100 Subject: [PATCH 25/68] allow custom pre-setup config options --- README.md | 1 + nix/hm-module.nix | 9 ++++++++- nix/image/configs/nc/config.php | 4 ++-- src/cloud.rs | 12 ++++++++++-- src/config.rs | 4 ++++ 5 files changed, 25 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 3bb98ca..0ad77bc 100644 --- a/README.md +++ b/README.md @@ -321,6 +321,7 @@ disable_apps = ["contacts"] # apps to disable after setup, defaults to [] post_setup = [# commands to execute after setup, defaults to [] "occ group:add test", ] +config = { "foo" = "bar" } # configuration options to set before install [[volume]] # optional source = "/tmp/haze-shared" diff --git a/nix/hm-module.nix b/nix/hm-module.nix index 072fda4..7fe684d 100644 --- a/nix/hm-module.nix +++ b/nix/hm-module.nix @@ -12,7 +12,7 @@ with lib; let app_directories = cfg.appDirectories; work_dir = cfg.workDir; auto_setup = { - enabled = cfg.autoSetup.enable; + inherit (cfg.autoSetup) enable config; post_setup = cfg.autoSetup.postSetup; enable_apps = cfg.autoSetup.enableApps; disable_apps = cfg.autoSetup.disableApps; @@ -91,6 +91,13 @@ in { default = []; description = "Commands to run post-setup"; }; + config = mkOption { + type = types.submodule { + freeformType = format.type; + }; + description = "Configuration options to set before install"; + default = {}; + }; }; }; }; diff --git a/nix/image/configs/nc/config.php b/nix/image/configs/nc/config.php index b00d47b..06de6db 100644 --- a/nix/image/configs/nc/config.php +++ b/nix/image/configs/nc/config.php @@ -9,8 +9,8 @@ if (file_exists(__DIR__ . '/nextcloud.json')) { $CONFIG = array_merge_recursive($extra_config, [ 'debug' => true, 'memcache.local' => '\\OC\\Memcache\\APCu', - 'memcache.distributed' => '\\OC\\Memcache\\APCu', - 'memcache.locking' => '\\OC\\Memcache\\APCu', + 'memcache.distributed' => '\\OC\\Memcache\\Redis', + 'memcache.locking' => '\\OC\\Memcache\\Redis', 'allow_local_remote_servers' => true, 'trusted_domains' => ['cloud'], 'profiling.secret' => 'haze', diff --git a/src/cloud.rs b/src/cloud.rs index 8437d63..0c52e13 100644 --- a/src/cloud.rs +++ b/src/cloud.rs @@ -15,7 +15,7 @@ use flate2::read::GzDecoder; use futures_util::future::try_join_all; use miette::{IntoDiagnostic, Report, Result, WrapErr}; use petname::petname; -use serde_json::{Map, Value}; +use serde_json::Value; use std::borrow::Cow; use std::collections::HashMap; use std::fmt::Display; @@ -297,7 +297,15 @@ impl Cloud { .wrap_err_with(|| format!("Failed to setup work directory {}", mapping.source))?; } - let mut nc_config = Value::Object(Map::new()); + let mut nc_config = Value::Object( + config + .auto_setup + .config + .clone() + .into_iter() + .map(|(key, value)| (key, serde_json::to_value(value).unwrap())) + .collect(), + ); nc_config["apps_paths"] = Value::Array( once("apps") .chain( diff --git a/src/config.rs b/src/config.rs index b790b52..9e4548f 100644 --- a/src/config.rs +++ b/src/config.rs @@ -7,6 +7,7 @@ use std::convert::TryFrom; use std::env::home_dir; use std::fs::read_to_string; use std::net::IpAddr; +use toml::map::Map; use toml::Value; #[derive(Debug, Deserialize, Default)] @@ -87,6 +88,8 @@ pub struct HazeAutoSetupConfig { pub disable_apps: Vec, #[serde(default)] pub post_setup: Vec, + #[serde(default)] + pub config: Map, } impl Default for HazeAutoSetupConfig { @@ -98,6 +101,7 @@ impl Default for HazeAutoSetupConfig { enable_apps: Vec::default(), disable_apps: Vec::default(), post_setup: Vec::default(), + config: Map::default(), } } } From 7e54fbd89fd0850505c169c99eeb267be381d880 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Mon, 9 Mar 2026 20:10:23 +0100 Subject: [PATCH 26/68] add s3 TLS option --- README.md | 9 ++- certificates/s3/private.key | 28 ++++++++ certificates/s3/public.crt | 21 ++++++ nix/image/configs/nc/s3s.php | 15 ++++ nix/image/scripts/nc-auto-config | 5 ++ src/cloud.rs | 16 +++++ src/service.rs | 12 ++++ src/service/objectstore.rs | 113 +++++++++++++++++++++++++++---- 8 files changed, 203 insertions(+), 16 deletions(-) create mode 100644 certificates/s3/private.key create mode 100644 certificates/s3/public.crt create mode 100644 nix/image/configs/nc/s3s.php diff --git a/README.md b/README.md index 0ad77bc..7c5e334 100644 --- a/README.md +++ b/README.md @@ -76,9 +76,9 @@ Additionally, you can use the following options when starting an instance: - `s3`: set up an S3 server and configure to Nextcloud to use it as primary storage. -- ``: by specifying the path to an app package this package - will be extracted into the apps. directory of the new instance (overwriting - any existing app code). This can be used to quickly test a packaged app. + - `s3s`: enable TLS for the S3 setup. + - `s3mb`: enable multi-bucket S3 setup. + - `s3m`: enable multi-instance S3 setup. - `ldap`: set up an LDAP server. - `office`: set up a Nextcloud Office server. - `onlyoffice` setup an onlyoffice document server. @@ -100,6 +100,9 @@ Additionally, you can use the following options when starting an instance: configure it the mail server. - `redis`: start a separate container for redis. - `redis-tls`: connect to redis over TLS. +- ``: by specifying the path to an app package this package + will be extracted into the apps. directory of the new instance (overwriting + any existing app code). This can be used to quickly test a packaged app. - The name of any configured preset. #### Run tests in a new instance diff --git a/certificates/s3/private.key b/certificates/s3/private.key new file mode 100644 index 0000000..939d93e --- /dev/null +++ b/certificates/s3/private.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCQuTk+irdXRGbY +JUu+AFmMM4/CCtBIBBgGIG18tesgQUeHEPdRHbBypvFKhpXvlKQbVbiaZxFjtlvn +L2ReN7gYwjiTjLWuaDgOzGQyObwSJpedlcd5Q957WNlc5OjpoK8zZq9EGnmvjIqe +5VVBne85RZVw6+i4ljEiWoXCiy0iOPIL4jKO8kfO8EmTTh6ge2sLCT5jFT/V63/B +hjEobA7+vPbAmEo+Qs4adnBSrlX9nLtL1j4gqawAQMmGl/Ti75T0uQvNxdq2gddc +n0wyWhyERnZNeMv5sfkwCOTuetNNtGLf/lTkXNdaOCRvW5jJ0EjYYdpQG5R0do1U +XFLH1cZZAgMBAAECggEAR0xpTk2Ku5yASlY9dXK4qyCv3znymLgjmckaB4mcN7zR +X1JVdYn55tImJ8AcV/bTzn+xvaevYn9x0XiAqwYqVVBCDTcSPsUrcObzKedVp1+J +7GHg7vYnwn7oPyKrOIYoKluZVyTv9DN6C4QSN4x2UbHdSM+ATIf51uHf6hMk/ilv +4uw3csxSCpLOqqsYCQarES7SypcETjFpNnIfTPt7q2Y2DbIDttTTjzrm0/GBP0WE +OYNvzZZPZRPJfy3et3r9vJbqWzGHvOttzQ+EFHjwPTMfW4tsHcCsEKSGWwLpG4bN +FPNx0+QCqDiChesdiCHFNSk+u7pRZrHdjuDJEuKSzQKBgQDMDgdCGBQfgaNnkRz/ +aiv3V200/vXegnc0Jz49Dye5AxEVu0X1m2xZpJv+qEwbOx5B+1PV3gfVP/iRf+FK +MAwFbmb7hGcDE8AGNsSpQydjwzKoi/M67YXv7T8dgWKnwz0eyU4K2IOGInGxuFty +Ik7+DTqz+Ikh1RiAoGbKfw9yhwKBgQC1kKhjxB7r/uSLcfOSg1mLcR5lTrNDQAPQ +GnsIje0nD5Tv52/k6U4tk15vjL4t6KZUFo9SJ4O1kM1veyuOJuol2AfPXS+H/Izo +5BjmoZ0jOONOZZiRIB1moQSy1qhTAeZB9S1ORxQ3dIBPqm+oyADPTTsNV67Cwnt4 +woeZRUPYHwKBgQDE0AcKJcVK+jQMUXfBlrsfTvDjO8MTwYyN/gfWxsZOeXnCFyYM +FcO01sMrJVJ6tVOi2nFrB0NQ2Om8FLbMYnlFx82GbJca7bK5i5u1kjLs0zoKPSn1 +vWEBIDhPEhuAqhxKlGk0ps580r4MZz+0XwkHmuTy7xX9TtbaQVvDljflAwKBgDKy +3hJdpTTIzBCUFSuIOezR/WbUfwH8UhQ+ELTmzJ3nn/MNcRU+gHIBgJEtf71aBXfd +hM+v8Ps2H+dNQXBENYWzuRqSLr+OKdquNrXP0w0OyYoOnHeJvCv4MlOt1Pq4wQ8R +40DEYETL5zhXoy5CCtfX/PFQ1p/Tpp6l0y9dRACJAoGALwUbyyDy85b2xRQB6RtU +I+5Vz5cd/1eQdCkoU9mX4qWA/hWpgc7Z2Jd67LW/WWtVjlF9hva/WNDSfGsXo2ew +C8OofvlfIuFDOCXrodYdHE1Q4g5TZdESr0XAqopq+QzBs89qbIy05kM9iuE4yFUo +xeimCY9oDWTeGw/XrLdHZF4= +-----END PRIVATE KEY----- diff --git a/certificates/s3/public.crt b/certificates/s3/public.crt new file mode 100644 index 0000000..f31f379 --- /dev/null +++ b/certificates/s3/public.crt @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDXTCCAkWgAwIBAgIULiChaTwmVx6nRTHohmPuf55/4jUwDQYJKoZIhvcNAQEL +BQAwPjELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxDTALBgNVBAoM +BEhhemUxCzAJBgNVBAMMAnMzMB4XDTI2MDMwOTE3NTgwOVoXDTI3MDMwOTE3NTgw +OVowPjELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxDTALBgNVBAoM +BEhhemUxCzAJBgNVBAMMAnMzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEAkLk5Poq3V0Rm2CVLvgBZjDOPwgrQSAQYBiBtfLXrIEFHhxD3UR2wcqbxSoaV +75SkG1W4mmcRY7Zb5y9kXje4GMI4k4y1rmg4DsxkMjm8EiaXnZXHeUPee1jZXOTo +6aCvM2avRBp5r4yKnuVVQZ3vOUWVcOvouJYxIlqFwostIjjyC+IyjvJHzvBJk04e +oHtrCwk+YxU/1et/wYYxKGwO/rz2wJhKPkLOGnZwUq5V/Zy7S9Y+IKmsAEDJhpf0 +4u+U9LkLzcXatoHXXJ9MMlochEZ2TXjL+bH5MAjk7nrTTbRi3/5U5FzXWjgkb1uY +ydBI2GHaUBuUdHaNVFxSx9XGWQIDAQABo1MwUTAdBgNVHQ4EFgQUJJ8HiT2zmuF5 +6WHHFsTHgkrayxYwHwYDVR0jBBgwFoAUJJ8HiT2zmuF56WHHFsTHgkrayxYwDwYD +VR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAUF0lB/qIrxkgZ4sqNrw4 +CInHCK29XVaMoqk1QZyS/KhWDM+zgbA92OxxuhCKw4iJEajZvgg0S9RtGkBNmquU +l0rf0JdALd0jPkWr7+3OeqlcgOs2EH7PTqrrbXTGsR12D+Ot+OerQeWXmO28Zrl8 +4O67TwQtslXwZzeCrtiwAA2DrIYpSLzh+qDtwbY5hMG5zmqqjBM20Ysgxszh4rhl +KR6skXwZwkVVhKpK76qwnU02PIMr8auL1csx8/uBTd/UzX2veqlkOP5V/Gg6eEbI +4fTOzq7k+FyuzSkrEX4Vc9GbWcRvoVZh+qAKUKstqlE2iCrqmZ+Wal6GA8JA5SZ+ +bQ== +-----END CERTIFICATE----- diff --git a/nix/image/configs/nc/s3s.php b/nix/image/configs/nc/s3s.php new file mode 100644 index 0000000..cfbf186 --- /dev/null +++ b/nix/image/configs/nc/s3s.php @@ -0,0 +1,15 @@ + 'objectstore' => [ + 'class' => 'OC\Files\ObjectStore\S3', + 'arguments' => [ + 'bucket' => 'nextcloud', + 'autocreate' => true, + 'key' => 'minio', + 'secret' => 'minio123', + 'hostname' => 's3', + 'port' => 9000, + 'use_ssl' => true, + 'use_path_style' => true, + 'uploadPartSize' => 52428800, + 'use_nextcloud_bundle' => true, + ], + ], diff --git a/nix/image/scripts/nc-auto-config b/nix/image/scripts/nc-auto-config index 6a803d9..fc8d95c 100755 --- a/nix/image/scripts/nc-auto-config +++ b/nix/image/scripts/nc-auto-config @@ -32,6 +32,11 @@ then sed -i '/\/\/PLACEHOLDER/ r /etc/nc/s3.php' /var/www/html/config/config.php fi +if [ -n "${S3S:-}" ] +then + sed -i '/\/\/PLACEHOLDER/ r /etc/nc/s3s.php' /var/www/html/config/config.php +fi + if [ -n "${S3MB:-}" ] then sed -i '/\/\/PLACEHOLDER/ r /etc/nc/s3mb.php' /var/www/html/config/config.php diff --git a/src/cloud.rs b/src/cloud.rs index 0c52e13..cb5e2f7 100644 --- a/src/cloud.rs +++ b/src/cloud.rs @@ -498,6 +498,22 @@ impl Cloud { } }; + for pre_setup in options + .services + .iter() + .flat_map(|service| service.pre_setup(docker, &id, config).into_iter().flatten()) + { + exec( + docker, + &container, + &uid.to_string(), + pre_setup, + vec!["NC_IS_CONFIG_READ_ONLY=1"], + Some(stdout()), + ) + .await?; + } + containers.push(container); let options_clone = options.clone(); diff --git a/src/service.rs b/src/service.rs index 344ec49..67325c3 100644 --- a/src/service.rs +++ b/src/service.rs @@ -100,6 +100,15 @@ pub trait ServiceTrait { Ok(HashMap::default()) } + fn pre_setup( + &self, + _docker: &Docker, + _cloud_id: &str, + _config: &HazeConfig, + ) -> Result>> { + Ok(Vec::new()) + } + async fn post_setup( &self, _docker: &Docker, @@ -205,6 +214,8 @@ impl ServiceTrait for RedisTls { pub enum ServiceType { /// S3 Primary storage and external storage S3, + /// S3 Primary storage with TLS + S3s, /// S3 multi-object store Primary storage and external storage S3m, /// S3 multi-bucket Primary storage and external storage @@ -307,6 +318,7 @@ impl Service { if let Ok(ty) = ServiceType::from_str(ty) { match ty { ServiceType::S3 => Some(vec![Service::ObjectStore(ObjectStore::S3)]), + ServiceType::S3s => Some(vec![Service::ObjectStore(ObjectStore::S3s)]), ServiceType::S3m => Some(vec![Service::ObjectStore(ObjectStore::S3m)]), ServiceType::S3mb => Some(vec![Service::ObjectStore(ObjectStore::S3mb)]), ServiceType::Azure => Some(vec![Service::ObjectStore(ObjectStore::Azure)]), diff --git a/src/service/objectstore.rs b/src/service/objectstore.rs index 4f0b7d1..c0767e0 100644 --- a/src/service/objectstore.rs +++ b/src/service/objectstore.rs @@ -10,11 +10,15 @@ use bollard::models::{ use bollard::query_parameters::CreateContainerOptions; use bollard::Docker; use maplit::hashmap; -use miette::IntoDiagnostic; +use miette::{IntoDiagnostic, WrapErr}; +use serde_json::Value; +use std::collections::HashMap; +use std::fs::{create_dir_all, write}; #[derive(Debug, Clone, Eq, PartialEq)] pub enum ObjectStore { S3, + S3s, S3m, S3mb, Azure, @@ -23,7 +27,7 @@ pub enum ObjectStore { impl ObjectStore { fn image(&self) -> &str { match self { - ObjectStore::S3 | ObjectStore::S3m | ObjectStore::S3mb => { + ObjectStore::S3 | ObjectStore::S3m | ObjectStore::S3mb | ObjectStore::S3s => { "minio/minio:RELEASE.2024-07-16T23-46-41Z" } ObjectStore::Azure => "arafato/azurite:2.6.5", @@ -32,7 +36,7 @@ impl ObjectStore { fn self_env(&self) -> Vec<&str> { match self { - ObjectStore::S3 | ObjectStore::S3m | ObjectStore::S3mb => { + ObjectStore::S3 | ObjectStore::S3m | ObjectStore::S3mb | ObjectStore::S3s => { vec!["MINIO_ACCESS_KEY=minio", "MINIO_SECRET_KEY=minio123"] } ObjectStore::Azure => vec![], @@ -41,17 +45,54 @@ impl ObjectStore { fn host_name(&self) -> &str { match self { - ObjectStore::S3 | ObjectStore::S3m | ObjectStore::S3mb => "s3", + ObjectStore::S3 | ObjectStore::S3m | ObjectStore::S3mb | ObjectStore::S3s => "s3", ObjectStore::Azure => "azure", } } fn args(&self) -> &[&str] { match self { - ObjectStore::S3 | ObjectStore::S3m | ObjectStore::S3mb => &["server", "/data"], + ObjectStore::S3 | ObjectStore::S3m | ObjectStore::S3mb | ObjectStore::S3s => { + &["server", "/data"] + } _ => &[], } } + + fn volumes(&self, config: &HazeConfig) -> Option> { + match self { + ObjectStore::S3s => { + let cert_dir = config.work_dir.join("certificates/s3"); + create_dir_all(&cert_dir) + .into_diagnostic() + .wrap_err("Failed to create redis certificate directory") + .unwrap(); + let s3_cert_path = config.work_dir.join("certificates/s3/public.crt"); + let s3_key_path = config.work_dir.join("certificates/s3/private.key"); + if !s3_cert_path.exists() { + write( + &s3_cert_path, + include_bytes!("../../certificates/s3/public.crt"), + ) + .into_diagnostic() + .wrap_err("Failed to write s3 certificate") + .unwrap(); + } + if !s3_key_path.exists() { + write( + &s3_key_path, + include_bytes!("../../certificates/s3/private.key"), + ) + .into_diagnostic() + .wrap_err("Failed to write s3 key") + .unwrap(); + } + + Some(vec![format!("{cert_dir}:/root/.minio/certs:ro")]) + } + _ => None, + } + } } #[async_trait::async_trait] @@ -59,6 +100,7 @@ impl ServiceTrait for ObjectStore { fn name(&self) -> &str { match self { ObjectStore::S3 => "s3", + ObjectStore::S3s => "s3s", ObjectStore::S3m => "s3m", ObjectStore::S3mb => "s3mb", ObjectStore::Azure => "azure", @@ -68,8 +110,9 @@ impl ServiceTrait for ObjectStore { fn env(&self) -> &[&str] { match self { ObjectStore::S3 => &["S3=1"], + ObjectStore::S3s => &["S3S=1"], ObjectStore::S3m => &["S3M=1"], - ObjectStore::S3mb => &["S3MB=1"], + ObjectStore::S3mb => &["S3MB =1"], ObjectStore::Azure => &["AZURE=1"], } } @@ -79,7 +122,7 @@ impl ServiceTrait for ObjectStore { docker: &Docker, cloud_id: &str, network: &str, - _config: &HazeConfig, + config: &HazeConfig, _options: &CloudOptions, ) -> Result> { pull_image(docker, self.image()).await?; @@ -92,6 +135,7 @@ impl ServiceTrait for ObjectStore { env: Some(self.self_env().into_iter().map(String::from).collect()), host_config: Some(HostConfig { network_mode: Some(network.to_string()), + binds: self.volumes(config), ..Default::default() }), labels: Some(hashmap! { @@ -165,14 +209,47 @@ impl ServiceTrait for ObjectStore { &["files_external"] } + fn config( + &self, + _docker: &Docker, + _cloud_id: &str, + _config: &HazeConfig, + ) -> Result> { + match self { + ObjectStore::S3s => Ok(hashmap![ + "default_certificates_bundle_path".into() => Value::String("/var/www/html/data/ca-bundle.crt".into()), + ]), + _ => Ok(HashMap::default()), + } + } + + fn pre_setup( + &self, + _docker: &Docker, + _cloud_id: &str, + _config: &HazeConfig, + ) -> Result>> { + match self { + ObjectStore::S3s => Ok(vec![ + vec!["mkdir".into(), "-p".into(), "/var/www/html/data".into()], + vec![ + "sh".into(), + "-c".into(), + "cat /var/www/html/resources/config/ca-bundle.crt /certificates/s3/public.crt > /var/www/html/data/ca-bundle.crt".into(), + ], + ]), + _ => Ok(Vec::new()), + } + } + async fn post_setup( &self, _docker: &Docker, _cloud_id: &str, _config: &HazeConfig, ) -> Result> { - if *self == ObjectStore::S3 { - Ok(vec![ + match self { + ObjectStore::S3 => Ok(vec![ "occ files_external:create s3 amazons3 amazons3::accesskey".into(), "occ files_external:config 1 bucket ext".into(), "occ files_external:config 1 hostname s3".into(), @@ -182,15 +259,25 @@ impl ServiceTrait for ObjectStore { "occ files_external:config 1 key minio".into(), "occ files_external:config 1 secret minio123".into(), "mc alias set s3 http://s3:9000 minio minio123".into(), - ]) - } else { - Ok(Vec::new()) + ]), + // ObjectStore::S3s => Ok(vec![ + // "occ files_external:create s3 amazons3 amazons3::accesskey".into(), + // "occ files_external:config 1 bucket ext".into(), + // "occ files_external:config 1 hostname s3".into(), + // "occ files_external:config 1 port 9000".into(), + // "occ files_external:config 1 use_ssl true".into(), + // "occ files_external:config 1 use_path_style true".into(), + // "occ files_external:config 1 key minio".into(), + // "occ files_external:config 1 secret minio123".into(), + // "mc alias set s3 https://s3:9000 minio minio123".into(), + // ]), + _ => Ok(Vec::new()), } } fn proxy_port(&self) -> u16 { match self { - ObjectStore::S3 | ObjectStore::S3m | ObjectStore::S3mb => 9000, + ObjectStore::S3 | ObjectStore::S3m | ObjectStore::S3mb | ObjectStore::S3s => 9000, ObjectStore::Azure => 10000, } } From 8780fe0754556c9db6a562f691b726a05868563b Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Mon, 9 Mar 2026 22:48:27 +0100 Subject: [PATCH 27/68] build new 8.0 and 8.1 images --- .forgejo/workflows/docker.yaml | 2 +- README.md | 6 +-- flake.lock | 88 +++++++++++++++++++++++++++++++++- flake.nix | 14 +++++- nix/image/php.nix | 4 ++ nix/overlay.nix | 1 + 6 files changed, 108 insertions(+), 7 deletions(-) diff --git a/.forgejo/workflows/docker.yaml b/.forgejo/workflows/docker.yaml index dfbdb95..7944c61 100644 --- a/.forgejo/workflows/docker.yaml +++ b/.forgejo/workflows/docker.yaml @@ -16,7 +16,7 @@ jobs: strategy: matrix: - php-version: ["8.2", "8.3", "8.4"] + php-version: ["8.0", "8,1", "8.2", "8.3", "8.4"] variant: [""] name: haze-${{ matrix.php-version }}${{ matrix.variant }} diff --git a/README.md b/README.md index 7c5e334..6407db8 100644 --- a/README.md +++ b/README.md @@ -61,10 +61,8 @@ haze start [database] [php-version] [services] [vX.Y.Z] Where `database` is one of `sqlite`, `mysql`, `mariadb`, `pgsql` or `oracle` with an optional version (e.g. `pgsql:12`), defaults to `sqlite`. And -`php-version` is one of `8.2`, `8.3` or `8.4`, defaults to the maximum version -support by the current Nextcloud version. `7.3` till `8.1` are still supported -but the docker images for those versions aren't being updated anymore so they -might be missing some newer features. +`php-version` is one of `8.0`, `8.1`, `8.2`, `8.3` or `8.4`, defaults to the +maximum version support by the current Nextcloud version. Each php version also comes with a `-dbg` variant that has php compiled in debug mode and can be used for debugging php itself with gdb. diff --git a/flake.lock b/flake.lock index 81e86a7..4ffb480 100644 --- a/flake.lock +++ b/flake.lock @@ -15,6 +15,22 @@ "type": "github" } }, + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1767039857, + "narHash": "sha256-vNpUSpF5Nuw8xvDLj2KCwwksIbjua2LZCqhV1LNRDns=", + "owner": "NixOS", + "repo": "flake-compat", + "rev": "5edf11c44bc78a0d334f6334cdaf7d60d732daab", + "type": "github" + }, + "original": { + "owner": "NixOS", + "repo": "flake-compat", + "type": "github" + } + }, "flakelight": { "inputs": { "nixpkgs": [ @@ -72,11 +88,48 @@ "type": "indirect" } }, + "nixpkgs_2": { + "locked": { + "lastModified": 1772956932, + "narHash": "sha256-M0yS4AafhKxPPmOHGqIV0iKxgNO8bHDWdl1kOwGBwRY=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "608d0cadfed240589a7eea422407a547ad626a14", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "phps": { + "inputs": { + "flake-compat": "flake-compat", + "nixpkgs": "nixpkgs_2", + "utils": "utils" + }, + "locked": { + "lastModified": 1772365008, + "narHash": "sha256-/ynkWKeZ1dyRIUkQas0AB35semWAwCbTKXu+/q+8MGg=", + "owner": "fossar", + "repo": "nix-phps", + "rev": "f47eb877bf1c219809e4357eec2fdab8e3263b7b", + "type": "github" + }, + "original": { + "owner": "fossar", + "repo": "nix-phps", + "type": "github" + } + }, "root": { "inputs": { "flakelight": "flakelight", "mill-scale": "mill-scale", - "nixpkgs": "nixpkgs" + "nixpkgs": "nixpkgs", + "phps": "phps" } }, "rust-overlay": { @@ -100,6 +153,39 @@ "repo": "rust-overlay", "type": "github" } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } } }, "root": "root", diff --git a/flake.nix b/flake.nix index 4d2f05b..991f5e8 100644 --- a/flake.nix +++ b/flake.nix @@ -9,8 +9,15 @@ url = "git+https://codeberg.org/icewind/mill-scale.git"; inputs.flakelight.follows = "flakelight"; }; + phps = { + url = "github:fossar/nix-phps"; + }; }; - outputs = {mill-scale, ...}: + outputs = { + mill-scale, + phps, + ... + }: mill-scale ./. { crossTargets = [ "x86_64-unknown-linux-gnu" @@ -29,12 +36,17 @@ withOverlays = [ (import ./nix/overlay.nix) + (prev: final: { + inherit (phps.packages.${prev.system}) php81 php80; + }) ]; packages = { "haze-image-php-8.4" = pkgs: pkgs.haze-image-php-84; "haze-image-php-8.3" = pkgs: pkgs.haze-image-php-83; "haze-image-php-8.2" = pkgs: pkgs.haze-image-php-82; + "haze-image-php-8.1" = pkgs: pkgs.haze-image-php-81; + "haze-image-php-8.0" = pkgs: pkgs.haze-image-php-80; }; tools = pkgs: with pkgs; [cargo-edit bacon skopeo]; diff --git a/nix/image/php.nix b/nix/image/php.nix index 852b478..4fa7ff6 100644 --- a/nix/image/php.nix +++ b/nix/image/php.nix @@ -3,7 +3,9 @@ php, debug ? false, }: let + inherit (builtins) compareVersions; inherit (lib) optionals; + withBlackfire = !debug && ((compareVersions php.version "8.1.0") == 1); in php.buildEnv { extensions = { @@ -36,6 +38,8 @@ in ] ++ optionals (!debug) [ smbclient # this breaks the build for no apparent reason + ] + ++ optionals withBlackfire [ blackfire ]); extraConfig = '' diff --git a/nix/overlay.nix b/nix/overlay.nix index b08daca..1f927f8 100644 --- a/nix/overlay.nix +++ b/nix/overlay.nix @@ -4,4 +4,5 @@ final: prev: { haze-image-php-83 = final.callPackage ./image/haze.nix {php = final.php83;}; haze-image-php-82 = final.callPackage ./image/haze.nix {php = final.php82;}; haze-image-php-81 = final.callPackage ./image/haze.nix {php = final.php81;}; + haze-image-php-80 = final.callPackage ./image/haze.nix {php = final.php80;}; } From da6c6d754bde428e088d06b8a3b3fa92ccf52b9a Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Mon, 9 Mar 2026 23:00:21 +0100 Subject: [PATCH 28/68] add 8.5 image --- .forgejo/workflows/docker.yaml | 2 +- README.md | 4 ++-- flake.lock | 38 +++++++++++++++++----------------- flake.nix | 1 + nix/image/bootstrap.sh | 2 +- nix/overlay.nix | 1 + src/php.rs | 38 +++++++++++----------------------- 7 files changed, 37 insertions(+), 49 deletions(-) diff --git a/.forgejo/workflows/docker.yaml b/.forgejo/workflows/docker.yaml index 7944c61..4c8b3b2 100644 --- a/.forgejo/workflows/docker.yaml +++ b/.forgejo/workflows/docker.yaml @@ -16,7 +16,7 @@ jobs: strategy: matrix: - php-version: ["8.0", "8,1", "8.2", "8.3", "8.4"] + php-version: ["8.0", "8,1", "8.2", "8.3", "8.4", "8.5"] variant: [""] name: haze-${{ matrix.php-version }}${{ matrix.variant }} diff --git a/README.md b/README.md index 6407db8..87debd4 100644 --- a/README.md +++ b/README.md @@ -61,8 +61,8 @@ haze start [database] [php-version] [services] [vX.Y.Z] Where `database` is one of `sqlite`, `mysql`, `mariadb`, `pgsql` or `oracle` with an optional version (e.g. `pgsql:12`), defaults to `sqlite`. And -`php-version` is one of `8.0`, `8.1`, `8.2`, `8.3` or `8.4`, defaults to the -maximum version support by the current Nextcloud version. +`php-version` is one of `8.0`, `8.1`, `8.2`, `8.3`, `8.4` or `8.5`, defaults to +the maximum version support by the current Nextcloud version. Each php version also comes with a `-dbg` variant that has php compiled in debug mode and can be used for debugging php itself with gdb. diff --git a/flake.lock b/flake.lock index 4ffb480..a7766b8 100644 --- a/flake.lock +++ b/flake.lock @@ -2,11 +2,11 @@ "nodes": { "crane": { "locked": { - "lastModified": 1760924934, - "narHash": "sha256-tuuqY5aU7cUkR71sO2TraVKK2boYrdW3gCSXUkF4i44=", + "lastModified": 1763938834, + "narHash": "sha256-j8iB0Yr4zAvQLueCZ5abxfk6fnG/SJ5JnGUziETjwfg=", "owner": "ipetkov", "repo": "crane", - "rev": "c6b4d5308293d0d04fcfeee92705017537cad02f", + "rev": "d9e753122e51cee64eb8d2dddfe11148f339f5a2", "type": "github" }, "original": { @@ -38,11 +38,11 @@ ] }, "locked": { - "lastModified": 1764593611, - "narHash": "sha256-6SdexcO69Dlu14YN2xuB1A6JHWSrcqMj7Na9oK7IT2M=", + "lastModified": 1773062095, + "narHash": "sha256-u+cK9IoJokO4YzQwMc2s8Vti0RL/LVSrROOEn2opc5U=", "owner": "nix-community", "repo": "flakelight", - "rev": "0d63256401341f528dd628f1a8e96d3afecade7a", + "rev": "c99e4d5f40e578cb2d8f460ea2bbd5dc26316d24", "type": "github" }, "original": { @@ -60,11 +60,11 @@ "rust-overlay": "rust-overlay" }, "locked": { - "lastModified": 1763591898, - "narHash": "sha256-aHSMj7CIa9EJYxdf05wOWRGp0KRsT/TAox7uwVSdDb8=", + "lastModified": 1772297202, + "narHash": "sha256-UEzHO/tCmhPhr8RpWtbm1MTa7ABobwt3nCjrcuDAPm0=", "ref": "refs/heads/main", - "rev": "2d9b2da2c9f384f93ef977c48f8ee35ce586529b", - "revCount": 66, + "rev": "8690e1514863b934de12f2a503c9431d186ce30b", + "revCount": 68, "type": "git", "url": "https://codeberg.org/icewind/mill-scale.git" }, @@ -75,11 +75,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1764522689, - "narHash": "sha256-SqUuBFjhl/kpDiVaKLQBoD8TLD+/cTUzzgVFoaHrkqY=", + "lastModified": 1772822230, + "narHash": "sha256-yf3iYLGbGVlIthlQIk5/4/EQDZNNEmuqKZkQssMljuw=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "8bb5646e0bed5dbd3ab08c7a7cc15b75ab4e1d0f", + "rev": "71caefce12ba78d84fe618cf61644dce01cf3a96", "type": "github" }, "original": { @@ -90,11 +90,11 @@ }, "nixpkgs_2": { "locked": { - "lastModified": 1772956932, - "narHash": "sha256-M0yS4AafhKxPPmOHGqIV0iKxgNO8bHDWdl1kOwGBwRY=", + "lastModified": 1772173633, + "narHash": "sha256-MOH58F4AIbCkh6qlQcwMycyk5SWvsqnS/TCfnqDlpj4=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "608d0cadfed240589a7eea422407a547ad626a14", + "rev": "c0f3d81a7ddbc2b1332be0d8481a672b4f6004d6", "type": "github" }, "original": { @@ -141,11 +141,11 @@ ] }, "locked": { - "lastModified": 1761964689, - "narHash": "sha256-Zo3LQQDz+64EQ9zor/WmeNTFLoZkjmhp0UY3G0D3seE=", + "lastModified": 1764557621, + "narHash": "sha256-kX5PoY8hQZ80+amMQgOO9t8Tc1JZ70gYRnzaVD4AA+o=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "63d22578600f70d293aede6bc737efef60ebd97f", + "rev": "93316876c2229460a5d6f5f052766cc4cef538ce", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 991f5e8..9934eb7 100644 --- a/flake.nix +++ b/flake.nix @@ -42,6 +42,7 @@ ]; packages = { + "haze-image-php-8.5" = pkgs: pkgs.haze-image-php-85; "haze-image-php-8.4" = pkgs: pkgs.haze-image-php-84; "haze-image-php-8.3" = pkgs: pkgs.haze-image-php-83; "haze-image-php-8.2" = pkgs: pkgs.haze-image-php-82; diff --git a/nix/image/bootstrap.sh b/nix/image/bootstrap.sh index 50c14c2..a00a950 100755 --- a/nix/image/bootstrap.sh +++ b/nix/image/bootstrap.sh @@ -9,7 +9,7 @@ echo "# Options in here overwrite the builtin php.ini" > /config/php.ini echo "# xdebug.mode = debug" >> /config/php.ini echo "# xdebug.start_with_request = yes" >> /config/php.ini chmod 0777 /config/php.ini -PHP_INI_DIR="$(php --ini | grep 'Scan' | cut -d ' ' -f7)" +PHP_INI_DIR="$(php --ini | grep 'Scan' | cut -d ' ' -f7 | tr -d '"')" ln -s /config/php.ini "$PHP_INI_DIR/zz_extra.ini" HAZE_UID=${HAZE_UID:-www-data} diff --git a/nix/overlay.nix b/nix/overlay.nix index 1f927f8..276b70e 100644 --- a/nix/overlay.nix +++ b/nix/overlay.nix @@ -1,5 +1,6 @@ final: prev: { haze = final.callPackage ./package.nix {}; + haze-image-php-85 = final.callPackage ./image/haze.nix {php = final.php85;}; haze-image-php-84 = final.callPackage ./image/haze.nix {php = final.php84;}; haze-image-php-83 = final.callPackage ./image/haze.nix {php = final.php83;}; haze-image-php-82 = final.callPackage ./image/haze.nix {php = final.php82;}; diff --git a/src/php.rs b/src/php.rs index 0842ec2..2d037bc 100644 --- a/src/php.rs +++ b/src/php.rs @@ -21,20 +21,18 @@ use tokio::time::{sleep, timeout}; #[allow(dead_code)] pub enum PhpVersion { #[default] + Php85, Php84, Php83, Php82, Php81, Php80, - Php74, - Php73, + Php85Dbg, Php84Dbg, Php83Dbg, Php82Dbg, Php81Dbg, Php80Dbg, - Php74Dbg, - Php73Dbg, } pub const PHP_MEMORY_LIMIT: i64 = 2 * 1024 * 1024 * 1024; @@ -44,24 +42,20 @@ impl FromStr for PhpVersion { fn from_str(s: &str) -> Result { match s { - "7" => Ok(PhpVersion::Php74), - "7.3" => Ok(PhpVersion::Php73), - "7.4" => Ok(PhpVersion::Php74), "8" => Ok(PhpVersion::Php81), "8.0" => Ok(PhpVersion::Php80), "8.1" => Ok(PhpVersion::Php81), "8.2" => Ok(PhpVersion::Php82), "8.3" => Ok(PhpVersion::Php83), "8.4" => Ok(PhpVersion::Php84), - "7-dbg" => Ok(PhpVersion::Php74Dbg), - "7.3-dbg" => Ok(PhpVersion::Php73Dbg), - "7.4-dbg" => Ok(PhpVersion::Php74Dbg), + "8.5" => Ok(PhpVersion::Php84), "8-dbg" => Ok(PhpVersion::Php80Dbg), "8.0-dbg" => Ok(PhpVersion::Php80Dbg), "8.1-dbg" => Ok(PhpVersion::Php81Dbg), "8.2-dbg" => Ok(PhpVersion::Php82Dbg), "8.3-dbg" => Ok(PhpVersion::Php83Dbg), "8.4-dbg" => Ok(PhpVersion::Php84Dbg), + "8.5-dbg" => Ok(PhpVersion::Php85Dbg), _ => Err(()), } } @@ -73,53 +67,47 @@ impl PhpVersion { } pub fn image(&self) -> &'static str { - // for now only 7.4 match self { - PhpVersion::Php73 => "icewind1991/haze:7.3", - PhpVersion::Php74 => "icewind1991/haze:7.4", PhpVersion::Php80 => "icewind1991/haze:8.0", PhpVersion::Php81 => "icewind1991/haze:8.1", PhpVersion::Php82 => "icewind1991/haze:8.2", PhpVersion::Php83 => "icewind1991/haze:8.3", PhpVersion::Php84 => "icewind1991/haze:8.4", - PhpVersion::Php73Dbg => "icewind1991/haze:7.3-dbg", - PhpVersion::Php74Dbg => "icewind1991/haze:7.4-dbg", + PhpVersion::Php85 => "icewind1991/haze:8.5", PhpVersion::Php80Dbg => "icewind1991/haze:8.0-dbg", PhpVersion::Php81Dbg => "icewind1991/haze:8.1-dbg", PhpVersion::Php82Dbg => "icewind1991/haze:8.2-dbg", PhpVersion::Php83Dbg => "icewind1991/haze:8.3-dbg", PhpVersion::Php84Dbg => "icewind1991/haze:8.4-dbg", + PhpVersion::Php85Dbg => "icewind1991/haze:8.5-dbg", } } pub fn name(&self) -> &'static str { match self { - PhpVersion::Php73 => "7.3", - PhpVersion::Php74 => "7.4", PhpVersion::Php80 => "8.0", PhpVersion::Php81 => "8.1", PhpVersion::Php82 => "8.2", PhpVersion::Php83 => "8.3", PhpVersion::Php84 => "8.4", - PhpVersion::Php73Dbg => "7.3-dbg", - PhpVersion::Php74Dbg => "7.4-dbg", + PhpVersion::Php85 => "8.4", PhpVersion::Php80Dbg => "8.0-dbg", PhpVersion::Php81Dbg => "8.1-dbg", PhpVersion::Php82Dbg => "8.2-dbg", PhpVersion::Php83Dbg => "8.3-dbg", PhpVersion::Php84Dbg => "8.4-dbg", + PhpVersion::Php85Dbg => "8.4-dbg", } } pub fn from_number(major: u8, minor: u8) -> Option { match (major, minor) { - (7, 3) => Some(PhpVersion::Php73), - (7, 4) => Some(PhpVersion::Php74), (8, 0) => Some(PhpVersion::Php80), (8, 1) => Some(PhpVersion::Php81), (8, 2) => Some(PhpVersion::Php82), (8, 3) => Some(PhpVersion::Php83), (8, 4) => Some(PhpVersion::Php84), + (8, 5) => Some(PhpVersion::Php85), _ => None, } } @@ -127,27 +115,25 @@ impl PhpVersion { pub fn max_minor(major: u8) -> u8 { match major { 7 => 4, - 8 => 4, + 8 => 5, _ => 0, } } pub fn all() -> impl Iterator { [ - PhpVersion::Php73, - PhpVersion::Php74, PhpVersion::Php80, PhpVersion::Php81, PhpVersion::Php82, PhpVersion::Php83, PhpVersion::Php84, - PhpVersion::Php73Dbg, - PhpVersion::Php74Dbg, + PhpVersion::Php85, PhpVersion::Php80Dbg, PhpVersion::Php81Dbg, PhpVersion::Php82Dbg, PhpVersion::Php83Dbg, PhpVersion::Php84Dbg, + PhpVersion::Php85Dbg, ] .into_iter() } From ef86840e77d9c0b029ba5587d94dab57b06675e0 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Mon, 9 Mar 2026 23:05:38 +0100 Subject: [PATCH 29/68] dont fail cleaning stopped containers --- src/cloud.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/cloud.rs b/src/cloud.rs index cb5e2f7..ada7520 100644 --- a/src/cloud.rs +++ b/src/cloud.rs @@ -563,11 +563,9 @@ impl Cloud { pub async fn destroy(self, docker: &Docker) -> Result<()> { for container in self.containers { - docker + let _ = docker .kill_container(container.trim_start_matches('/'), None) - .await - .into_diagnostic() - .wrap_err("Failed to kill container")?; + .await; docker .remove_container( container.trim_start_matches('/'), From 00964146140390a46fa84b97ac620abe100b7424 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Mon, 9 Mar 2026 23:17:32 +0100 Subject: [PATCH 30/68] -dbg images are no longer a thing --- README.md | 3 --- src/php.rs | 33 +-------------------------------- 2 files changed, 1 insertion(+), 35 deletions(-) diff --git a/README.md b/README.md index 87debd4..839e91c 100644 --- a/README.md +++ b/README.md @@ -64,9 +64,6 @@ with an optional version (e.g. `pgsql:12`), defaults to `sqlite`. And `php-version` is one of `8.0`, `8.1`, `8.2`, `8.3`, `8.4` or `8.5`, defaults to the maximum version support by the current Nextcloud version. -Each php version also comes with a `-dbg` variant that has php compiled in debug -mode and can be used for debugging php itself with gdb. - You can specify a version number (e.g. `v32.0.2`) to use the sources from a release instead of using the local sources. diff --git a/src/php.rs b/src/php.rs index 2d037bc..b461c72 100644 --- a/src/php.rs +++ b/src/php.rs @@ -27,12 +27,6 @@ pub enum PhpVersion { Php82, Php81, Php80, - Php85Dbg, - Php84Dbg, - Php83Dbg, - Php82Dbg, - Php81Dbg, - Php80Dbg, } pub const PHP_MEMORY_LIMIT: i64 = 2 * 1024 * 1024 * 1024; @@ -48,14 +42,7 @@ impl FromStr for PhpVersion { "8.2" => Ok(PhpVersion::Php82), "8.3" => Ok(PhpVersion::Php83), "8.4" => Ok(PhpVersion::Php84), - "8.5" => Ok(PhpVersion::Php84), - "8-dbg" => Ok(PhpVersion::Php80Dbg), - "8.0-dbg" => Ok(PhpVersion::Php80Dbg), - "8.1-dbg" => Ok(PhpVersion::Php81Dbg), - "8.2-dbg" => Ok(PhpVersion::Php82Dbg), - "8.3-dbg" => Ok(PhpVersion::Php83Dbg), - "8.4-dbg" => Ok(PhpVersion::Php84Dbg), - "8.5-dbg" => Ok(PhpVersion::Php85Dbg), + "8.5" => Ok(PhpVersion::Php85), _ => Err(()), } } @@ -74,12 +61,6 @@ impl PhpVersion { PhpVersion::Php83 => "icewind1991/haze:8.3", PhpVersion::Php84 => "icewind1991/haze:8.4", PhpVersion::Php85 => "icewind1991/haze:8.5", - PhpVersion::Php80Dbg => "icewind1991/haze:8.0-dbg", - PhpVersion::Php81Dbg => "icewind1991/haze:8.1-dbg", - PhpVersion::Php82Dbg => "icewind1991/haze:8.2-dbg", - PhpVersion::Php83Dbg => "icewind1991/haze:8.3-dbg", - PhpVersion::Php84Dbg => "icewind1991/haze:8.4-dbg", - PhpVersion::Php85Dbg => "icewind1991/haze:8.5-dbg", } } @@ -91,12 +72,6 @@ impl PhpVersion { PhpVersion::Php83 => "8.3", PhpVersion::Php84 => "8.4", PhpVersion::Php85 => "8.4", - PhpVersion::Php80Dbg => "8.0-dbg", - PhpVersion::Php81Dbg => "8.1-dbg", - PhpVersion::Php82Dbg => "8.2-dbg", - PhpVersion::Php83Dbg => "8.3-dbg", - PhpVersion::Php84Dbg => "8.4-dbg", - PhpVersion::Php85Dbg => "8.4-dbg", } } @@ -128,12 +103,6 @@ impl PhpVersion { PhpVersion::Php83, PhpVersion::Php84, PhpVersion::Php85, - PhpVersion::Php80Dbg, - PhpVersion::Php81Dbg, - PhpVersion::Php82Dbg, - PhpVersion::Php83Dbg, - PhpVersion::Php84Dbg, - PhpVersion::Php85Dbg, ] .into_iter() } From 87de2dbb216e1c5360d8a8359e443cbf4aab02fe Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Tue, 10 Mar 2026 14:09:11 +0100 Subject: [PATCH 31/68] fix tests --- src/cloud.rs | 20 ++++++++++---------- src/php.rs | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/cloud.rs b/src/cloud.rs index ada7520..afa01cf 100644 --- a/src/cloud.rs +++ b/src/cloud.rs @@ -152,44 +152,44 @@ fn test_option_parse() { ..Default::default() } ); - let mut args = vec!["7"].into_iter().peekable(); + let mut args = vec!["8"].into_iter().peekable(); assert_eq!( CloudOptions::parse(&config, &mut args).unwrap(), CloudOptions { - php: PhpVersion::Php74, + php: PhpVersion::Php80, ..Default::default() } ); - let mut args = vec!["7", "pgsql", "rest"].into_iter().peekable(); + let mut args = vec!["8.1", "pgsql", "rest"].into_iter().peekable(); assert_eq!( CloudOptions::parse(&config, &mut args).unwrap(), CloudOptions { - php: PhpVersion::Php74, + php: PhpVersion::Php81, db: Database::Postgres, ..Default::default() } ); - let mut args = vec!["7", "ldap", "pgsql"].into_iter().peekable(); + let mut args = vec!["8", "ldap", "pgsql"].into_iter().peekable(); assert_eq!( CloudOptions::parse(&config, &mut args).unwrap(), CloudOptions { - php: PhpVersion::Php74, + php: PhpVersion::Php80, db: Database::Postgres, services: vec![Service::Ldap(Ldap), Service::LdapAdmin(LdapAdmin)], ..Default::default() } ); - let mut args = vec!["7", "pgsql", "ldap"].into_iter().peekable(); + let mut args = vec!["8", "pgsql", "ldap"].into_iter().peekable(); assert_eq!( CloudOptions::parse(&config, &mut args).unwrap(), CloudOptions { - php: PhpVersion::Php74, + php: PhpVersion::Php80, db: Database::Postgres, services: vec![Service::Ldap(Ldap), Service::LdapAdmin(LdapAdmin)], ..Default::default() } ); - let mut args = vec!["7", "pgsql", "ldap", "mypreset"] + let mut args = vec!["8", "pgsql", "ldap", "mypreset"] .into_iter() .peekable(); @@ -206,7 +206,7 @@ fn test_option_parse() { assert_eq!( CloudOptions::parse(&config, &mut args).unwrap(), CloudOptions { - php: PhpVersion::Php74, + php: PhpVersion::Php80, db: Database::Postgres, services: vec![ Service::Ldap(Ldap), diff --git a/src/php.rs b/src/php.rs index b461c72..1451108 100644 --- a/src/php.rs +++ b/src/php.rs @@ -36,7 +36,7 @@ impl FromStr for PhpVersion { fn from_str(s: &str) -> Result { match s { - "8" => Ok(PhpVersion::Php81), + "8" => Ok(PhpVersion::Php80), "8.0" => Ok(PhpVersion::Php80), "8.1" => Ok(PhpVersion::Php81), "8.2" => Ok(PhpVersion::Php82), From 4635ecf3fcb4aeb25a183cbb745d07a2a4a2c28b Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Tue, 10 Mar 2026 14:50:25 +0100 Subject: [PATCH 32/68] 2.2.0 --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d73ab8d..aba8178 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -932,7 +932,7 @@ checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" [[package]] name = "haze" -version = "2.1.6" +version = "2.2.0" dependencies = [ "async-trait", "atty", diff --git a/Cargo.toml b/Cargo.toml index 5760bd8..73a4c8d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "haze" -version = "2.1.6" +version = "2.2.0" authors = ["Robin Appelman "] edition = "2021" repository = "https://codeberg.org/icewind/haze" From 85ffdcea5a0e9da66170d2c07a56adc94bdb2576 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Tue, 10 Mar 2026 15:05:13 +0100 Subject: [PATCH 33/68] fix workflow typo --- .forgejo/workflows/docker.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.forgejo/workflows/docker.yaml b/.forgejo/workflows/docker.yaml index 4c8b3b2..5d88645 100644 --- a/.forgejo/workflows/docker.yaml +++ b/.forgejo/workflows/docker.yaml @@ -16,7 +16,7 @@ jobs: strategy: matrix: - php-version: ["8.0", "8,1", "8.2", "8.3", "8.4", "8.5"] + php-version: ["8.0", "8.1", "8.2", "8.3", "8.4", "8.5"] variant: [""] name: haze-${{ matrix.php-version }}${{ matrix.variant }} From 0105c60a094915f7fc1fb617bc967c32d7557d84 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Tue, 10 Mar 2026 15:06:39 +0100 Subject: [PATCH 34/68] workflow cleanup --- .forgejo/workflows/docker.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.forgejo/workflows/docker.yaml b/.forgejo/workflows/docker.yaml index 5d88645..c2dbdbd 100644 --- a/.forgejo/workflows/docker.yaml +++ b/.forgejo/workflows/docker.yaml @@ -17,9 +17,8 @@ jobs: strategy: matrix: php-version: ["8.0", "8.1", "8.2", "8.3", "8.4", "8.5"] - variant: [""] - name: haze-${{ matrix.php-version }}${{ matrix.variant }} + name: haze-${{ matrix.php-version }} steps: - name: Checkout repository From 6fdadd9bad9cc4a0872573d2cae9132101608588 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Thu, 12 Mar 2026 00:21:54 +0100 Subject: [PATCH 35/68] use supervisord in image --- nix/image/bootstrap.sh | 20 ++++----------- nix/image/configs.nix | 7 ++++-- nix/image/{ => configs}/nginx.conf | 0 nix/image/{ => configs}/php-fpm.conf | 0 nix/image/configs/supervisor/blackfire.conf | 2 ++ nix/image/configs/supervisor/redis-plain.conf | 2 ++ nix/image/configs/supervisor/redis-tls.conf | 6 +++++ nix/image/configs/supervisor/supervisord.conf | 25 +++++++++++++++++++ nix/image/haze.nix | 2 ++ 9 files changed, 47 insertions(+), 17 deletions(-) rename nix/image/{ => configs}/nginx.conf (100%) rename nix/image/{ => configs}/php-fpm.conf (100%) create mode 100644 nix/image/configs/supervisor/blackfire.conf create mode 100644 nix/image/configs/supervisor/redis-plain.conf create mode 100644 nix/image/configs/supervisor/redis-tls.conf create mode 100644 nix/image/configs/supervisor/supervisord.conf diff --git a/nix/image/bootstrap.sh b/nix/image/bootstrap.sh index a00a950..c8842f9 100755 --- a/nix/image/bootstrap.sh +++ b/nix/image/bootstrap.sh @@ -28,8 +28,6 @@ echo "{}" > /var/www/html/build/integration/composer.lock echo "Starting server using $SQL database…" -# tail --follow --retry /var/log/nginx/*.log /var/log/cron/owncloud.log & - chmod +sx /sbin/sudo mkdir -p /var/log/nginx /tmp /var/run/blackfire @@ -57,23 +55,15 @@ fi if [ -n "${REDIS_TLS:-}" ] then - redis-server --protected-mode no \ - --tls-port 6379 --port 0 \ - --tls-cert-file /certificates/redis/server.crt \ - --tls-key-file /certificates/redis/server.key \ - --tls-ca-cert-file /certificates/redis/ca.crt & + cp /etc/supervisor/redis-tls.conf /etc/supervisor/enabled/ else - redis-server --protected-mode no & + cp /etc/supervisor/redis-plain.conf /etc/supervisor/enabled/ fi if [ -n "${BLACKFIRE_SERVER_ID:-}" ] then - sh -c ' - yes | blackfire agent:config --server-id=$BLACKFIRE_SERVER_ID --server-token=$BLACKFIRE_SERVER_TOKEN - BLACKFIRE_LOG_LEVEL=4 BLACKFIRE_LOG_FILE=/var/log/agent.log blackfire agent & - '& + blackfire agent:config --server-id="$BLACKFIRE_SERVER_ID" --server-token="$BLACKFIRE_SERVER_TOKEN" + cp /etc/supervisor/blackfire.conf /etc/supervisor/enabled/ fi -php-fpm --fpm-config /etc/php-fpm.conf& - -nginx -c /etc/nginx.conf \ No newline at end of file +exec supervisord -c /etc/supervisor/supervisord.conf \ No newline at end of file diff --git a/nix/image/configs.nix b/nix/image/configs.nix index 1b90bc8..79449b5 100644 --- a/nix/image/configs.nix +++ b/nix/image/configs.nix @@ -7,6 +7,9 @@ runCommand "configs" {} '' cp ${./configs/nginx-app.conf} $out/conf/nginx-app.conf cp ${./configs/sudoers} $out/etc/sudoers.d/haze cp -r ${./configs/nc} $out/etc/nc - cp ${./php-fpm.conf} $out/etc/php-fpm.conf - cp ${./nginx.conf} $out/etc/nginx.conf + cp ${./configs/php-fpm.conf} $out/etc/php-fpm.conf + cp ${./configs/nginx.conf} $out/etc/nginx.conf + cp -r ${./configs/supervisor} $out/etc/supervisor + chmod +w $out/etc/supervisor + mkdir $out/etc/supervisor/enabled/ '' diff --git a/nix/image/nginx.conf b/nix/image/configs/nginx.conf similarity index 100% rename from nix/image/nginx.conf rename to nix/image/configs/nginx.conf diff --git a/nix/image/php-fpm.conf b/nix/image/configs/php-fpm.conf similarity index 100% rename from nix/image/php-fpm.conf rename to nix/image/configs/php-fpm.conf diff --git a/nix/image/configs/supervisor/blackfire.conf b/nix/image/configs/supervisor/blackfire.conf new file mode 100644 index 0000000..ca6cd89 --- /dev/null +++ b/nix/image/configs/supervisor/blackfire.conf @@ -0,0 +1,2 @@ +[program:blackfire] +command = blackfire agent \ No newline at end of file diff --git a/nix/image/configs/supervisor/redis-plain.conf b/nix/image/configs/supervisor/redis-plain.conf new file mode 100644 index 0000000..e9284cc --- /dev/null +++ b/nix/image/configs/supervisor/redis-plain.conf @@ -0,0 +1,2 @@ +[program:redis] +command = /bin/redis-server --protected-mode no \ No newline at end of file diff --git a/nix/image/configs/supervisor/redis-tls.conf b/nix/image/configs/supervisor/redis-tls.conf new file mode 100644 index 0000000..28d9be1 --- /dev/null +++ b/nix/image/configs/supervisor/redis-tls.conf @@ -0,0 +1,6 @@ +[program:redis-tls] +command = /bin/redis-server --protected-mode no \ + --tls-port 6379 --port 0 \ + --tls-cert-file /certificates/redis/server.crt \ + --tls-key-file /certificates/redis/server.key \ + --tls-ca-cert-file /certificates/redis/ca.crt \ No newline at end of file diff --git a/nix/image/configs/supervisor/supervisord.conf b/nix/image/configs/supervisor/supervisord.conf new file mode 100644 index 0000000..dee3c56 --- /dev/null +++ b/nix/image/configs/supervisor/supervisord.conf @@ -0,0 +1,25 @@ +[supervisord] +logfile = /dev/stdout +logfile_maxbytes = 0 +nodaemon = true +pidfile = /var/run/supervisord.pid + +[unix_http_server] +file = /var/run/supervisor.sock +chmod = 0777 + + +[rpcinterface:supervisor] +supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface + +[supervisorctl] +serverurl = unix:///var/run/supervisor.sock + +[program:nginx] +command = /bin/nginx -c /etc/nginx.conf + +[program:php-fpm] +command = /bin/php-fpm --fpm-config /etc/php-fpm.conf + +[include] +files = enabled/* diff --git a/nix/image/haze.nix b/nix/image/haze.nix index 926f0fa..ffdeb73 100644 --- a/nix/image/haze.nix +++ b/nix/image/haze.nix @@ -33,6 +33,7 @@ writeShellApplication, vim, helix, + python3Packages, }: let inherit (builtins) toString; inherit (lib) readFile getExe concatStringsSep splitString take; @@ -145,6 +146,7 @@ oracle-instantclient vim helix + python3Packages.supervisor ]; }; From 63e17d609fe0f4ebc03de810c0031b851147e053 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Fri, 20 Mar 2026 15:14:22 +0100 Subject: [PATCH 36/68] record haze version when building images and warn on out of date images --- nix/image/haze.nix | 11 ++++++++++- src/image.rs | 37 +++++++++++++++++++++++++++++++++++++ src/php.rs | 14 +++++++++++++- 3 files changed, 60 insertions(+), 2 deletions(-) diff --git a/nix/image/haze.nix b/nix/image/haze.nix index ffdeb73..7c740af 100644 --- a/nix/image/haze.nix +++ b/nix/image/haze.nix @@ -38,6 +38,8 @@ inherit (builtins) toString; inherit (lib) readFile getExe concatStringsSep splitString take; + version = (fromTOML (readFile ../../Cargo.toml)).package.version; + phpVersion = concatStringsSep "." (take 2 (splitString "." php.version)); phpEnv = callPackage ./php.nix {inherit debug php;}; @@ -187,7 +189,14 @@ in ''; config = { Cmd = [(getExe bootstrap)]; - Env = ["EDITOR=hx" "WEBROOT=/var/www/html"]; + Env = [ + "EDITOR=hx" + "WEBROOT=/var/www/html" + "HAZE_IMAGE_VERSION=${toString version}" + ]; WorkingDir = "/var/www/html"; + Labels = { + "nl.icewind.haze.version" = toString version; + }; }; } diff --git a/src/image.rs b/src/image.rs index 94da352..e24abfd 100644 --- a/src/image.rs +++ b/src/image.rs @@ -5,11 +5,48 @@ use futures_util::StreamExt; use indicatif::{MultiProgress, ProgressBar, ProgressStyle}; use miette::{IntoDiagnostic, Result, WrapErr}; use std::collections::HashMap; +use std::fmt::{Display, Formatter}; +use std::str::FromStr; + +#[derive(Debug, Copy, Clone, PartialOrd, PartialEq)] +pub struct ImageVersion { + pub major: u8, + pub minor: u8, + pub patch: u8, +} + +impl FromStr for ImageVersion { + type Err = (); + + fn from_str(s: &str) -> std::result::Result { + let mut parts = s.split('.'); + let major = parts.next().ok_or(())?.parse().map_err(|_| ())?; + let minor = parts.next().ok_or(())?.parse().map_err(|_| ())?; + let patch = parts.next().ok_or(())?.parse().map_err(|_| ())?; + Ok(ImageVersion { + major, + minor, + patch, + }) + } +} + +impl Display for ImageVersion { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "{}.{}.{}", self.major, self.minor, self.patch) + } +} pub async fn image_exists(docker: &Docker, image: &str) -> bool { docker.inspect_image(image).await.is_ok() } +pub async fn image_version(docker: &Docker, image: &str) -> Option { + let labels = docker.inspect_image(image).await.ok()?.config?.labels?; + let label = labels.get("nl.icewind.haze.version")?; + ImageVersion::from_str(label).ok() +} + pub async fn update_image(docker: &Docker, image: &str) -> Result<()> { if image_exists(docker, image).await { force_pull_image(docker, image).await?; diff --git a/src/php.rs b/src/php.rs index 1451108..ab01f79 100644 --- a/src/php.rs +++ b/src/php.rs @@ -1,6 +1,7 @@ +use owo_colors::OwoColorize; use crate::config::ProxyConfig; use crate::database::Database; -use crate::image::pull_image; +use crate::image::{image_version, pull_image, ImageVersion}; use crate::network::ensure_network_exists; use crate::service::Service; use crate::service::ServiceTrait; @@ -123,6 +124,17 @@ impl PhpVersion { ) -> Result { ensure_network_exists(docker, "haze").await?; pull_image(docker, self.image()).await?; + + let image_version = image_version(&docker, self.image()).await; + let haze_version = ImageVersion::from_str(env!("CARGO_PKG_VERSION")); + if let (Some(image_version), Ok(haze_version)) = (image_version, haze_version) { + if image_version < haze_version { + eprintln!("{}: image version is out of date, run {} to update.", "Warning".red(), "haze update".blue()); + eprintln!(" Haze version: {}", haze_version.bright_yellow()); + eprintln!(" Image version: {}", image_version.bright_yellow()); + } + } + let options = Some(CreateContainerOptions { name: Some(id.to_string()), ..CreateContainerOptions::default() From ce34f302a158d200ba19539ffc4cd81e77471a3e Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Fri, 20 Mar 2026 16:10:42 +0100 Subject: [PATCH 37/68] clippy fixes --- src/php.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/php.rs b/src/php.rs index ab01f79..f4e0277 100644 --- a/src/php.rs +++ b/src/php.rs @@ -125,7 +125,7 @@ impl PhpVersion { ensure_network_exists(docker, "haze").await?; pull_image(docker, self.image()).await?; - let image_version = image_version(&docker, self.image()).await; + let image_version = image_version(docker, self.image()).await; let haze_version = ImageVersion::from_str(env!("CARGO_PKG_VERSION")); if let (Some(image_version), Ok(haze_version)) = (image_version, haze_version) { if image_version < haze_version { From 0d9866765012c0906ef0f8c434b905aad52d9ef7 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Fri, 20 Mar 2026 21:21:26 +0100 Subject: [PATCH 38/68] add dns for proxy inside container --- nix/image/bootstrap.sh | 18 ++++++++++++++++++ nix/image/configs.nix | 14 +++----------- nix/image/configs/dnsmasq.conf | 6 ++++++ nix/image/configs/nginx.conf | 2 +- nix/image/configs/{cron.conf => oc-cron.conf} | 0 nix/image/configs/{sudoers => sudoers.d/haze} | 0 nix/image/configs/supervisor/dnsmasq.conf | 2 ++ nix/image/haze.nix | 2 ++ src/php.rs | 5 +++++ 9 files changed, 37 insertions(+), 12 deletions(-) create mode 100644 nix/image/configs/dnsmasq.conf rename nix/image/configs/{cron.conf => oc-cron.conf} (100%) rename nix/image/configs/{sudoers => sudoers.d/haze} (100%) create mode 100644 nix/image/configs/supervisor/dnsmasq.conf diff --git a/nix/image/bootstrap.sh b/nix/image/bootstrap.sh index c8842f9..51bfa68 100755 --- a/nix/image/bootstrap.sh +++ b/nix/image/bootstrap.sh @@ -66,4 +66,22 @@ then cp /etc/supervisor/blackfire.conf /etc/supervisor/enabled/ fi +if [ -n "${PROXY_BASE:-}" ]; then + UPSTREAM_DNS=$(cat /etc/resolv.conf | grep nameserver | cut -d' ' -f 2) + ( + RC=$(sed '/nameserver/d' /etc/resolv.conf) + echo "$RC" > /etc/resolv.conf + ) + echo 'nameserver 127.0.0.22' >> /etc/resolv.conf + + echo "s/UPSTREAM_DNS/${UPSTREAM_DNS}" + sed -i "s/UPSTREAM_DNS/${UPSTREAM_DNS}/" /etc/dnsmasq.conf + echo "s/PROXY_BASE/${PROXY_BASE}" + sed -i "s/PROXY_BASE/${PROXY_BASE}/" /etc/dnsmasq.conf + echo "s/HOST_IP/${HOST_IP}" + sed -i "s/HOST_IP/${HOST_IP}/" /etc/dnsmasq.conf + + cp /etc/supervisor/dnsmasq.conf /etc/supervisor/enabled/ +fi + exec supervisord -c /etc/supervisor/supervisord.conf \ No newline at end of file diff --git a/nix/image/configs.nix b/nix/image/configs.nix index 79449b5..afa06c7 100644 --- a/nix/image/configs.nix +++ b/nix/image/configs.nix @@ -1,15 +1,7 @@ {runCommand}: runCommand "configs" {} '' - mkdir -p $out/etc - mkdir -p $out/etc/sudoers.d - mkdir -p $out/conf - cp ${./configs/cron.conf} $out/etc/oc-cron.conf - cp ${./configs/nginx-app.conf} $out/conf/nginx-app.conf - cp ${./configs/sudoers} $out/etc/sudoers.d/haze - cp -r ${./configs/nc} $out/etc/nc - cp ${./configs/php-fpm.conf} $out/etc/php-fpm.conf - cp ${./configs/nginx.conf} $out/etc/nginx.conf - cp -r ${./configs/supervisor} $out/etc/supervisor - chmod +w $out/etc/supervisor + mkdir -p $out + cp -r ${./configs} $out/etc + chmod -R +w $out/etc mkdir $out/etc/supervisor/enabled/ '' diff --git a/nix/image/configs/dnsmasq.conf b/nix/image/configs/dnsmasq.conf new file mode 100644 index 0000000..2b1afc5 --- /dev/null +++ b/nix/image/configs/dnsmasq.conf @@ -0,0 +1,6 @@ +listen-address=127.0.0.22 +no-resolv + +address=/PROXY_BASE/HOST_IP + +server=UPSTREAM_DNS \ No newline at end of file diff --git a/nix/image/configs/nginx.conf b/nix/image/configs/nginx.conf index 6e707c2..b25ab52 100644 --- a/nix/image/configs/nginx.conf +++ b/nix/image/configs/nginx.conf @@ -79,7 +79,7 @@ http { } } - include /conf/nginx-app.conf; + include /etc/nginx-app.conf; location ~ \.php(?:$|/) { rewrite ^/(?!index|remote|public|cron|core\/ajax\/update|status|ocs\/v[12]|updater\/.+|ocs-provider\/.+|.+\/richdocumentscode\/proxy) /index.php$request_uri; diff --git a/nix/image/configs/cron.conf b/nix/image/configs/oc-cron.conf similarity index 100% rename from nix/image/configs/cron.conf rename to nix/image/configs/oc-cron.conf diff --git a/nix/image/configs/sudoers b/nix/image/configs/sudoers.d/haze similarity index 100% rename from nix/image/configs/sudoers rename to nix/image/configs/sudoers.d/haze diff --git a/nix/image/configs/supervisor/dnsmasq.conf b/nix/image/configs/supervisor/dnsmasq.conf new file mode 100644 index 0000000..d57ed09 --- /dev/null +++ b/nix/image/configs/supervisor/dnsmasq.conf @@ -0,0 +1,2 @@ +[program:dnsmasq] +command = /bin/dnsmasq --keep-in-foreground -u root \ No newline at end of file diff --git a/nix/image/haze.nix b/nix/image/haze.nix index 7c740af..8751e42 100644 --- a/nix/image/haze.nix +++ b/nix/image/haze.nix @@ -34,6 +34,7 @@ vim, helix, python3Packages, + dnsmasq, }: let inherit (builtins) toString; inherit (lib) readFile getExe concatStringsSep splitString take; @@ -149,6 +150,7 @@ vim helix python3Packages.supervisor + dnsmasq ]; }; diff --git a/src/php.rs b/src/php.rs index f4e0277..d1abaa2 100644 --- a/src/php.rs +++ b/src/php.rs @@ -160,6 +160,11 @@ impl PhpVersion { proxy_config.addr(id, IpAddr::V4(Ipv4Addr::LOCALHOST)) )); + env.push(format!("HOST_IP={host}")); + if !proxy_config.address.is_empty() { + env.push(format!("PROXY_BASE={}", proxy_config.address)); + } + let mut labels = hashmap! { "haze-type".to_string() => "cloud".to_string(), "haze-db".to_string() => db.name().to_string(), From 4ab23610a2d86c98d479d883dd9b444624185cba Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Sun, 22 Mar 2026 14:10:19 +0100 Subject: [PATCH 39/68] fix redis-tls not starting --- nix/image/configs/supervisor/redis-tls.conf | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/nix/image/configs/supervisor/redis-tls.conf b/nix/image/configs/supervisor/redis-tls.conf index 28d9be1..d9f86bd 100644 --- a/nix/image/configs/supervisor/redis-tls.conf +++ b/nix/image/configs/supervisor/redis-tls.conf @@ -1,6 +1,2 @@ [program:redis-tls] -command = /bin/redis-server --protected-mode no \ - --tls-port 6379 --port 0 \ - --tls-cert-file /certificates/redis/server.crt \ - --tls-key-file /certificates/redis/server.key \ - --tls-ca-cert-file /certificates/redis/ca.crt \ No newline at end of file +command = /bin/redis-server --protected-mode no --tls-port 6379 --port 0 --tls-cert-file /certificates/redis/server.crt --tls-key-file /certificates/redis/server.key --tls-ca-cert-file /certificates/redis/ca.crt \ No newline at end of file From 3b4014b5e40af42e2b7fa2a3bdd703cfc5682dc5 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Sun, 22 Mar 2026 14:10:19 +0100 Subject: [PATCH 40/68] basic frankenphp support fixes #17 --- nix/image/bootstrap.sh | 7 +++ nix/image/configs/supervisor/frankenphp.conf | 3 ++ nix/image/configs/supervisor/nginx.conf | 2 + nix/image/configs/supervisor/php-fpm.conf | 2 + nix/image/configs/supervisor/supervisord.conf | 7 --- nix/image/haze.nix | 7 +++ nix/image/php-ext.nix | 44 +++++++++++++++++++ nix/image/php.nix | 35 +-------------- src/service.rs | 17 +++++++ 9 files changed, 83 insertions(+), 41 deletions(-) create mode 100644 nix/image/configs/supervisor/frankenphp.conf create mode 100644 nix/image/configs/supervisor/nginx.conf create mode 100644 nix/image/configs/supervisor/php-fpm.conf create mode 100644 nix/image/php-ext.nix diff --git a/nix/image/bootstrap.sh b/nix/image/bootstrap.sh index 51bfa68..ce3d0e3 100755 --- a/nix/image/bootstrap.sh +++ b/nix/image/bootstrap.sh @@ -84,4 +84,11 @@ if [ -n "${PROXY_BASE:-}" ]; then cp /etc/supervisor/dnsmasq.conf /etc/supervisor/enabled/ fi +if [ -n "${FRANKENPHP:-}" ]; then + cp /etc/supervisor/frankenphp.conf /etc/supervisor/enabled/ +else + cp /etc/supervisor/php-fpm.conf /etc/supervisor/enabled/ + cp /etc/supervisor/nginx.conf /etc/supervisor/enabled/ +fi + exec supervisord -c /etc/supervisor/supervisord.conf \ No newline at end of file diff --git a/nix/image/configs/supervisor/frankenphp.conf b/nix/image/configs/supervisor/frankenphp.conf new file mode 100644 index 0000000..8c1b1e5 --- /dev/null +++ b/nix/image/configs/supervisor/frankenphp.conf @@ -0,0 +1,3 @@ +[program:frankenphp] +command = /bin/frankenphp php-server +directory = /var/www/html \ No newline at end of file diff --git a/nix/image/configs/supervisor/nginx.conf b/nix/image/configs/supervisor/nginx.conf new file mode 100644 index 0000000..957e4b3 --- /dev/null +++ b/nix/image/configs/supervisor/nginx.conf @@ -0,0 +1,2 @@ +[program:nginx] +command = /bin/nginx -c /etc/nginx.conf diff --git a/nix/image/configs/supervisor/php-fpm.conf b/nix/image/configs/supervisor/php-fpm.conf new file mode 100644 index 0000000..69418c7 --- /dev/null +++ b/nix/image/configs/supervisor/php-fpm.conf @@ -0,0 +1,2 @@ +[program:php-fpm] +command = /bin/php-fpm --fpm-config /etc/php-fpm.conf diff --git a/nix/image/configs/supervisor/supervisord.conf b/nix/image/configs/supervisor/supervisord.conf index dee3c56..2fedc90 100644 --- a/nix/image/configs/supervisor/supervisord.conf +++ b/nix/image/configs/supervisor/supervisord.conf @@ -8,18 +8,11 @@ pidfile = /var/run/supervisord.pid file = /var/run/supervisor.sock chmod = 0777 - [rpcinterface:supervisor] supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface [supervisorctl] serverurl = unix:///var/run/supervisor.sock -[program:nginx] -command = /bin/nginx -c /etc/nginx.conf - -[program:php-fpm] -command = /bin/php-fpm --fpm-config /etc/php-fpm.conf - [include] files = enabled/* diff --git a/nix/image/haze.nix b/nix/image/haze.nix index 8751e42..b78ec51 100644 --- a/nix/image/haze.nix +++ b/nix/image/haze.nix @@ -35,6 +35,7 @@ helix, python3Packages, dnsmasq, + frankenphp, }: let inherit (builtins) toString; inherit (lib) readFile getExe concatStringsSep splitString take; @@ -163,6 +164,12 @@ phpEnv phpEnv.packages.composer phpunit + (frankenphp.override { + php = php.withExtensions (import ./php-ext.nix { + inherit lib php; + enableBlackfire = false; + }); + }) ]; }; in diff --git a/nix/image/php-ext.nix b/nix/image/php-ext.nix new file mode 100644 index 0000000..2715cf2 --- /dev/null +++ b/nix/image/php-ext.nix @@ -0,0 +1,44 @@ +{ + lib, + php, + debug ? false, + enableBlackfire ? true, +}: let + inherit (builtins) compareVersions; + inherit (lib) optionals; + withBlackfire = enableBlackfire && !debug && ((compareVersions php.version "8.1.0") == 1); +in + { + enabled, + all, + }: + enabled + ++ (with all; + [ + xdebug + excimer + inotify + redis + oci8 + zip + pdo + pdo_pgsql + pdo_sqlite + pdo_mysql + pgsql + intl + curl + mbstring + pcntl + ldap + exif + gmp + apcu + ffi + ] + ++ optionals (!debug) [ + smbclient # this breaks the build for no apparent reason + ] + ++ optionals withBlackfire [ + blackfire + ]) diff --git a/nix/image/php.nix b/nix/image/php.nix index 4fa7ff6..4922655 100644 --- a/nix/image/php.nix +++ b/nix/image/php.nix @@ -8,40 +8,7 @@ withBlackfire = !debug && ((compareVersions php.version "8.1.0") == 1); in php.buildEnv { - extensions = { - enabled, - all, - }: - enabled - ++ (with all; - [ - xdebug - excimer - inotify - redis - oci8 - zip - pdo - pdo_pgsql - pdo_sqlite - pdo_mysql - pgsql - intl - curl - mbstring - pcntl - ldap - exif - gmp - apcu - ffi - ] - ++ optionals (!debug) [ - smbclient # this breaks the build for no apparent reason - ] - ++ optionals withBlackfire [ - blackfire - ]); + extensions = import ./php-ext.nix {inherit lib php debug;}; extraConfig = '' xdebug.mode=debug,trace,profile xdebug.start_with_request=trigger diff --git a/src/service.rs b/src/service.rs index 67325c3..49bb636 100644 --- a/src/service.rs +++ b/src/service.rs @@ -207,6 +207,19 @@ impl ServiceTrait for RedisTls { } } +#[derive(Clone, Eq, PartialEq, Debug)] +pub struct FrankenPhp; + +impl ServiceTrait for FrankenPhp { + fn name(&self) -> &str { + "franken-php" + } + + fn env(&self) -> &[&str] { + &["FRANKENPHP=1"] + } +} + #[derive( Copy, Clone, Debug, PartialEq, EnumString, EnumMessage, EnumIter, IntoStaticStr, Display, )] @@ -281,6 +294,8 @@ pub enum ServiceType { Redis, /// External redis instance with TLS RedisTls, + /// Use FrankenPHP instead of PHP-FPM + FrankenPhp, } #[enum_dispatch] @@ -310,6 +325,7 @@ pub enum Service { Mail(Mail), Redis(Redis), RedisTls(RedisTls), + FrankenPhp(FrankenPhp), Preset(PresetService), } @@ -352,6 +368,7 @@ impl Service { ServiceType::Mail => Some(vec![Service::Mail(Mail)]), ServiceType::Redis => Some(vec![Service::Redis(Redis)]), ServiceType::RedisTls => Some(vec![Service::RedisTls(RedisTls)]), + ServiceType::FrankenPhp => Some(vec![Service::FrankenPhp(FrankenPhp)]), } } else { presets From a80354c922d11c6c59fe38bb64b2de9d3d3c7fa9 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Wed, 25 Mar 2026 17:02:34 +0100 Subject: [PATCH 41/68] fix office --- src/service/office.rs | 64 ++++++++++++++++++++++++++----------------- 1 file changed, 39 insertions(+), 25 deletions(-) diff --git a/src/service/office.rs b/src/service/office.rs index 4cd72c9..9977f57 100644 --- a/src/service/office.rs +++ b/src/service/office.rs @@ -32,6 +32,23 @@ impl ServiceTrait for Office { config: &HazeConfig, _options: &CloudOptions, ) -> Result> { + let network_info = docker + .inspect_network(&network, None) + .await + .into_diagnostic()?; + let gateway = network_info + .ipam + .as_ref() + .ok_or_else(|| Report::msg("Network has no ip info"))? + .config + .as_deref() + .ok_or_else(|| Report::msg("Network has no ip info"))? + .first() + .ok_or_else(|| Report::msg("Network has no ip info"))? + .gateway + .as_deref() + .ok_or_else(|| Report::msg("Network has no ip info"))?; + let image = "collabora/code"; pull_image(docker, image).await?; let container_id = self.container_name(cloud_id).unwrap(); @@ -39,28 +56,31 @@ impl ServiceTrait for Office { name: Some(container_id.clone()), ..CreateContainerOptions::default() }); - let mut env = vec!["extra_params=--o:ssl.enable=false --o:ssl.termination=true"]; + let mut env = + vec![r#"extra_params=--o:ssl.enable=false --o:ssl.termination=true --o:net.frame_ancestors=*"#.to_string()]; + let proxy_base = &config.proxy.address; let clean_id = container_id.strip_prefix("haze-").unwrap_or(&container_id); - let server_name_opt = match (&config.proxy.address, config.proxy.https) { - (public, true) if !public.is_empty() => { - format!("server_name={clean_id}.{public}") - } - (public, false) if !public.is_empty() => { - format!("server_name={clean_id}.{public}") - } - _ => "".to_string(), - }; - - if !server_name_opt.is_empty() { - env.push(&server_name_opt); + if !proxy_base.is_empty() { + env.push(format!("server_name={clean_id}.{}", config.proxy.address)); } + let clean_cloud_id = cloud_id.strip_prefix("haze-").unwrap_or(&cloud_id); + let hosts = if proxy_base.is_empty() { + vec![] + } else { + vec![ + format!("{proxy_base}:{gateway}"), + format!("{clean_cloud_id}.{proxy_base}:{gateway}"), + ] + }; + let config = ContainerCreateBody { image: Some(image.into()), env: Some(env.into_iter().map(String::from).collect()), host_config: Some(HostConfig { network_mode: Some(network.to_string()), + extra_hosts: Some(hosts), ..Default::default() }), labels: Some(hashmap! { @@ -127,19 +147,13 @@ impl ServiceTrait for Office { } else { return Err(Report::msg("office not started")); }; + let public = config + .proxy + .addr_with_port(container, ip, self.proxy_port()); + Ok(vec![ - format!( - r#"occ config:app:set richdocuments wopi_url --value="http://{}:9980""#, - ip - ), - format!( - r#"occ config:app:set richdocuments public_wopi_url --value="{}""#, - config.proxy.addr_with_port(container, ip, 9980) - ), - format!( - r#"occ config:app:set richdocuments wopi_root --value="http://{}""#, - cloud_id - ), + format!(r#"occ config:app:set richdocuments public_wopi_url --value="{public}""#), + r#"occ richdocuments:setup --wopi-url "http://office:9980" --callback-url "http://cloud""#.into(), ]) } From 3abf183ae34667f4436cd3f7e8d7c21250bf2878 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Fri, 10 Apr 2026 17:37:20 +0200 Subject: [PATCH 42/68] app integration support --- nix/image/haze.nix | 4 ++-- nix/image/scripts/integration | 20 +++++++++++++++++--- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/nix/image/haze.nix b/nix/image/haze.nix index b78ec51..3dd6567 100644 --- a/nix/image/haze.nix +++ b/nix/image/haze.nix @@ -8,9 +8,7 @@ coreutils, getent, shadow, - buildEnv, runCommand, - cacert, callPackage, cronie, redis, @@ -36,6 +34,7 @@ python3Packages, dnsmasq, frankenphp, + nushell, }: let inherit (builtins) toString; inherit (lib) readFile getExe concatStringsSep splitString take; @@ -152,6 +151,7 @@ helix python3Packages.supervisor dnsmasq + nushell ]; }; diff --git a/nix/image/scripts/integration b/nix/image/scripts/integration index 4f73aef..8477132 100755 --- a/nix/image/scripts/integration +++ b/nix/image/scripts/integration @@ -1,4 +1,18 @@ -#!/bin/sh +#!/bin/nu -cd $WEBROOT/build/integration -./run.sh "$@" +def main [feature: path, ...rest] { + mut feature = $feature; + mut workdir = $"($env.WEBROOT)/build/integration" + if ($feature | str starts-with "apps/") { + let parts = $feature | split row '/' + occ app:enable $parts.1 + let parts = $feature | split row -n 2 '/features/' + $workdir = $parts.0 + $feature = $"features/($parts.1)" + } else if ($feature | str starts-with "build/integration/") { + $feature = $feature | str replace "build/integration/" "" + } + + cd $workdir + bash run.sh $feature ...$rest +} From 266b70339bf00e5b6b9bdb46fd87351aac6b80a4 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Fri, 10 Apr 2026 17:40:35 +0200 Subject: [PATCH 43/68] cleanup --- nix/image/haze.nix | 8 -------- 1 file changed, 8 deletions(-) diff --git a/nix/image/haze.nix b/nix/image/haze.nix index 3dd6567..18ea63e 100644 --- a/nix/image/haze.nix +++ b/nix/image/haze.nix @@ -74,14 +74,6 @@ php = phpEnv; }; - phpunitWrapped = majorVersion: - writeShellApplication { - name = "phpunit${toString majorVersion}"; - text = '' - ${phpunitUnwrapped (toString majorVersion)}/bin/phpunit "$@" - ''; - }; - phpunit = writeShellApplication { name = "phpunit"; runtimeInputs = [jq]; From 0a16737398bd810b3055eadc2276f197763c6f89 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Mon, 13 Apr 2026 19:24:21 +0200 Subject: [PATCH 44/68] migrate scripts to nushell --- nix/image/bootstrap | 87 +++++++++++++++++ nix/image/bootstrap.sh | 94 ------------------- nix/image/configs/nc/redis-default.php | 1 - nix/image/configs/nc/redis-tls.php | 1 - nix/image/configs/supervisor/supervisord.conf | 1 + nix/image/haze.nix | 8 +- nix/image/scripts/install | 38 ++++---- nix/image/scripts/nc-auto-config | 80 +++++----------- nix/image/scripts/occ | 8 +- nix/image/scripts/tests | 10 +- 10 files changed, 143 insertions(+), 185 deletions(-) create mode 100755 nix/image/bootstrap delete mode 100755 nix/image/bootstrap.sh diff --git a/nix/image/bootstrap b/nix/image/bootstrap new file mode 100755 index 0000000..d16e605 --- /dev/null +++ b/nix/image/bootstrap @@ -0,0 +1,87 @@ +#!/bin/nu + +touch /var/log/nginx/access.log +touch /var/log/nginx/error.log +touch /var/log/cron/owncloud.log + +mkdir /config +echo "# Options in here overwrite the builtin php.ini\n" | save /config/php.ini +echo "# xdebug.mode = debug\n" | save -a /config/php.ini +echo "# xdebug.start_with_request = yes\n\n" | save -a /config/php.ini +chmod 0777 /config/php.ini +let PHP_INI_DIR = php --ini | grep 'Scan' | cut -d ' ' -f7 | tr -d '"' +ln -s /config/php.ini $"($PHP_INI_DIR)/zz_extra.ini" + +let HAZE_UID = $env.HAZE_UID | default "1000" +let HAZE_GID = $env.HAZE_GID | default "1000" + +nc-auto-config +shadow-setup + +echo $"Running as ($HAZE_UID):($HAZE_GID)" + +mkdir /var/www/html/core/skeleton /var/www/html/build/integration/vendor /var/www/html/build/integration/output /var/www/html/build/integration/work /var/www/html/core/skeleton /var/www/.composer/cache /var/www/html/apps/spreed/tests/integration/vendor/composer +chown -R $"($HAZE_UID):($HAZE_GID)" /var/www/html/data /var/www/html/config +chown $"($HAZE_UID):($HAZE_GID)" /var/www/html/core/skeleton /var/www/html/build/integration/vendor /var/www/html/build/integration/composer.lock /var/www/html/build/integration/output /var/www/html/build/integration/work /var/www/html/core/skeleton /var/www/.composer/cache /var/www/html/apps/spreed/tests/integration/vendor/composer + +echo "{}\n" | save -f /var/www/html/build/integration/composer.lock + +echo $"Starting server using ($env.SQL) database…" + +chmod +sx /sbin/sudo + +mkdir /var/log/nginx /tmp /var/run/blackfire +touch /var/log/nginx/access.log +touch /var/log/nginx/error.log + +if ((getent group $HAZE_GID | length) > 0) { + groupadd haze + useradd -u $HAZE_UID -g $HAZE_GID -G haze haze +} else { + groupadd -g $HAZE_GID haze + useradd -u $HAZE_UID -g $HAZE_GID haze +} +chown -R $"haze:($HAZE_GID)" /home/haze + +if ("/var/run/docker.sock" | path exists) { + let dockerGid = stat --format "%g" /var/run/docker.sock + groupadd docker -g $dockerGid + usermod -a -G docker haze +} + +if ("REDIS_TLS" in $env) { + cp /etc/supervisor/redis-tls.conf /etc/supervisor/enabled/ +} else { + cp /etc/supervisor/redis-plain.conf /etc/supervisor/enabled/ +} + +if ("BLACKFIRE_SERVER_ID" in $env) { + blackfire agent:config --server-id $env.BLACKFIRE_SERVER_ID --server-token $env.BLACKFIRE_SERVER_TOKEN + cp /etc/supervisor/blackfire.conf /etc/supervisor/enabled/ +} + +if ("PROXY_BASE" in $env) { + let UPSTREAM_DNS = cat /etc/resolv.conf | grep nameserver | cut -d' ' -f 2 + let RC = sed '/nameserver/d' /etc/resolv.conf + echo $RC | save -f /etc/resolv.conf + + echo "\nnameserver 127.0.0.22\n" | save -a /etc/resolv.conf + + echo $"s/UPSTREAM_DNS/($UPSTREAM_DNS)" + sed -i $"s/UPSTREAM_DNS/($UPSTREAM_DNS)/" /etc/dnsmasq.conf + echo $"s/PROXY_BASE/($env.PROXY_BASE)" + sed -i $"s/PROXY_BASE/($env.PROXY_BASE)/" /etc/dnsmasq.conf + echo $"s/HOST_IP/($env.HOST_IP)" + sed -i $"s/HOST_IP/($env.HOST_IP)/" /etc/dnsmasq.conf + + cp /etc/supervisor/dnsmasq.conf /etc/supervisor/enabled/ +} + +if ("FRANKENPHP" in $env) { + cp /etc/supervisor/frankenphp.conf /etc/supervisor/enabled/ +} else { + cp /etc/supervisor/php-fpm.conf /etc/supervisor/enabled/ + cp /etc/supervisor/nginx.conf /etc/supervisor/enabled/ +} + +exec supervisord -c /etc/supervisor/supervisord.conf diff --git a/nix/image/bootstrap.sh b/nix/image/bootstrap.sh deleted file mode 100755 index ce3d0e3..0000000 --- a/nix/image/bootstrap.sh +++ /dev/null @@ -1,94 +0,0 @@ -#!/usr/bin/env bash - -touch /var/log/nginx/access.log -touch /var/log/nginx/error.log -touch /var/log/cron/owncloud.log - -mkdir -p /config -echo "# Options in here overwrite the builtin php.ini" > /config/php.ini -echo "# xdebug.mode = debug" >> /config/php.ini -echo "# xdebug.start_with_request = yes" >> /config/php.ini -chmod 0777 /config/php.ini -PHP_INI_DIR="$(php --ini | grep 'Scan' | cut -d ' ' -f7 | tr -d '"')" -ln -s /config/php.ini "$PHP_INI_DIR/zz_extra.ini" - -HAZE_UID=${HAZE_UID:-www-data} -HAZE_GID=${HAZE_GID:-www-data} - -nc-auto-config -shadow-setup - -echo "Running as $HAZE_UID:$HAZE_GID" - -mkdir -p /var/www/html/core/skeleton /var/www/html/build/integration/vendor /var/www/html/build/integration/output /var/www/html/build/integration/work /var/www/html/core/skeleton /var/www/.composer/cache /var/www/html/apps/spreed/tests/integration/vendor/composer -chown -R "$HAZE_UID":"$HAZE_GID" /var/www/html/data /var/www/html/config -chown "$HAZE_UID":"$HAZE_GID" /var/www/html/core/skeleton /var/www/html/build/integration/vendor /var/www/html/build/integration/composer.lock /var/www/html/build/integration/output /var/www/html/build/integration/work /var/www/html/core/skeleton /var/www/.composer/cache /var/www/html/apps/spreed/tests/integration/vendor/composer - -echo "{}" > /var/www/html/build/integration/composer.lock - -echo "Starting server using $SQL database…" - -chmod +sx /sbin/sudo - -mkdir -p /var/log/nginx /tmp /var/run/blackfire -touch /var/log/nginx/access.log -touch /var/log/nginx/error.log - -HAZE_UID=${HAZE_UID:-1000} -HAZE_GID=${HAZE_GID:-1000} - -if [ "$(getent group "$HAZE_GID")" ]; then - groupadd haze - EXTRA_GROUP=" -G haze" -else - groupadd -g "$HAZE_GID" haze - EXTRA_GROUP="" -fi - -useradd -u "$HAZE_UID" -g "$HAZE_GID""$EXTRA_GROUP" haze -chown -R haze:"$HAZE_GID" /home/haze - -if [ -f "/var/run/docker.sock" ]; then - groupadd docker -g "$(stat --format "%g" /var/run/docker.sock)" - usermod -a -G docker haze -fi - -if [ -n "${REDIS_TLS:-}" ] -then - cp /etc/supervisor/redis-tls.conf /etc/supervisor/enabled/ -else - cp /etc/supervisor/redis-plain.conf /etc/supervisor/enabled/ -fi - -if [ -n "${BLACKFIRE_SERVER_ID:-}" ] -then - blackfire agent:config --server-id="$BLACKFIRE_SERVER_ID" --server-token="$BLACKFIRE_SERVER_TOKEN" - cp /etc/supervisor/blackfire.conf /etc/supervisor/enabled/ -fi - -if [ -n "${PROXY_BASE:-}" ]; then - UPSTREAM_DNS=$(cat /etc/resolv.conf | grep nameserver | cut -d' ' -f 2) - ( - RC=$(sed '/nameserver/d' /etc/resolv.conf) - echo "$RC" > /etc/resolv.conf - ) - echo 'nameserver 127.0.0.22' >> /etc/resolv.conf - - echo "s/UPSTREAM_DNS/${UPSTREAM_DNS}" - sed -i "s/UPSTREAM_DNS/${UPSTREAM_DNS}/" /etc/dnsmasq.conf - echo "s/PROXY_BASE/${PROXY_BASE}" - sed -i "s/PROXY_BASE/${PROXY_BASE}/" /etc/dnsmasq.conf - echo "s/HOST_IP/${HOST_IP}" - sed -i "s/HOST_IP/${HOST_IP}/" /etc/dnsmasq.conf - - cp /etc/supervisor/dnsmasq.conf /etc/supervisor/enabled/ -fi - -if [ -n "${FRANKENPHP:-}" ]; then - cp /etc/supervisor/frankenphp.conf /etc/supervisor/enabled/ -else - cp /etc/supervisor/php-fpm.conf /etc/supervisor/enabled/ - cp /etc/supervisor/nginx.conf /etc/supervisor/enabled/ -fi - -exec supervisord -c /etc/supervisor/supervisord.conf \ No newline at end of file diff --git a/nix/image/configs/nc/redis-default.php b/nix/image/configs/nc/redis-default.php index 2ba24aa..71b18a8 100644 --- a/nix/image/configs/nc/redis-default.php +++ b/nix/image/configs/nc/redis-default.php @@ -1,2 +1 @@ 'redis' => ['host' => 'localhost'], -//PLACEHOLDER diff --git a/nix/image/configs/nc/redis-tls.php b/nix/image/configs/nc/redis-tls.php index cb454ac..c3b9abe 100644 --- a/nix/image/configs/nc/redis-tls.php +++ b/nix/image/configs/nc/redis-tls.php @@ -8,4 +8,3 @@ 'verify_peer_name' => false, ], ], -//PLACEHOLDER diff --git a/nix/image/configs/supervisor/supervisord.conf b/nix/image/configs/supervisor/supervisord.conf index 2fedc90..bed7885 100644 --- a/nix/image/configs/supervisor/supervisord.conf +++ b/nix/image/configs/supervisor/supervisord.conf @@ -3,6 +3,7 @@ logfile = /dev/stdout logfile_maxbytes = 0 nodaemon = true pidfile = /var/run/supervisord.pid +user = root [unix_http_server] file = /var/run/supervisor.sock diff --git a/nix/image/haze.nix b/nix/image/haze.nix index 18ea63e..8c7551e 100644 --- a/nix/image/haze.nix +++ b/nix/image/haze.nix @@ -7,6 +7,7 @@ blackfire, coreutils, getent, + writers, shadow, runCommand, callPackage, @@ -44,11 +45,7 @@ phpVersion = concatStringsSep "." (take 2 (splitString "." php.version)); phpEnv = callPackage ./php.nix {inherit debug php;}; - bootstrap = writeShellApplication { - name = "bootstrap"; - runtimeInputs = [getent]; - text = readFile ./bootstrap.sh; - }; + bootstrap = writers.writeNuBin "bootstrap" (readFile ./bootstrap); shadowSetupScript = writeShellApplication { name = "shadow-setup"; text = dockerTools.shadowSetup; @@ -144,6 +141,7 @@ python3Packages.supervisor dnsmasq nushell + getent ]; }; diff --git a/nix/image/scripts/install b/nix/image/scripts/install index 2a86fcc..35d3f00 100755 --- a/nix/image/scripts/install +++ b/nix/image/scripts/install @@ -1,20 +1,22 @@ -#!/bin/sh +#!/bin/nu -USER=$1 -PASSWORD=$2 +def main [username: string, password: string] { + cd $env.WEBROOT; + let sql = match $env.SQL { + "oracle" => "oci" + "mariadb" => "mysql" + _ => $env.SQL + } + let dbName = match $env.SQL { + "oracle" => "xe" + _ => "haze" + } + let dbUser = match $env.SQL { + "oracle" => "system" + _ => "haze" + } + let dbPass = "haze" + let dbHost = $env.SQL -if [ -z "$USER" ] || [ -z "$PASSWORD" ]; then - echo "Usage: install \$USER \$PASSWORD" - exit; -fi - -cd $WEBROOT - -if [ "$SQL" = "oracle" ]; then - # oracle is a special snowflake - occ maintenance:install --admin-user=$USER --admin-pass=$PASSWORD --database=oci --database-name=xe --database-host=$SQL --database-user=system --database-pass=haze -elif [ "$SQL" = "mariadb" ]; then - occ maintenance:install --admin-user=$USER --admin-pass=$PASSWORD --database=mysql --database-name=haze --database-host=$SQL --database-user=haze --database-pass=haze -else - occ maintenance:install --admin-user=$USER --admin-pass=$PASSWORD --database=$SQL --database-name=haze --database-host=$SQL --database-user=haze --database-pass=haze -fi; + occ maintenance:install --admin-user $username --admin-pass $password --database $sql --database-name $dbName --database-host $dbHost --database-user $dbUser --database-pass $dbPass +} diff --git a/nix/image/scripts/nc-auto-config b/nix/image/scripts/nc-auto-config index fc8d95c..b7a2c07 100755 --- a/nix/image/scripts/nc-auto-config +++ b/nix/image/scripts/nc-auto-config @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#!/bin/nu touch /var/log/nginx/access.log touch /var/log/nginx/error.log @@ -7,64 +7,30 @@ touch /var/log/cron/owncloud.log cp /etc/nc/config.php /var/www/html/config/config.php chmod 0755 /var/www/html/config/config.php -if [ "$SQL" = "mysql" ] -then - cp /etc/nc/autoconfig_mysql.php /var/www/html/config/autoconfig.php -fi +let configName = match $env.SQL { + "oracle" => "oci" + _ => $env.SQL -if [ "$SQL" = "mariadb" ] -then - cp /etc/nc/autoconfig_mariadb.php /var/www/html/config/autoconfig.php -fi +} +let configPath = $"/etc/nc/autoconfig_($configName).php" -if [ "$SQL" = "pgsql" ] -then - cp /etc/nc/autoconfig_pgsql.php /var/www/html/config/autoconfig.php -fi +if ($configPath | path exists) { + cp $configPath /var/www/html/config/autoconfig.php +} -if [ "$SQL" = "oci" ] -then - cp /etc/nc/autoconfig_oci.php /var/www/html/config/autoconfig.php -fi +def loadExtraConfig [name: string] { + sed -i $'/\/\/PLACEHOLDER/ r /etc/nc/($name).php' /var/www/html/config/config.php +} -if [ -n "${S3:-}" ] -then - sed -i '/\/\/PLACEHOLDER/ r /etc/nc/s3.php' /var/www/html/config/config.php -fi +let extraConfigs = ["S3", "S3S", "S3MB", "S3M", "SWIFT", "SWIFTV3", "AZURE"]; +$extraConfigs | each { + if ($in in $env) { + loadExtraConfig ($in | str downcase) + } +} -if [ -n "${S3S:-}" ] -then - sed -i '/\/\/PLACEHOLDER/ r /etc/nc/s3s.php' /var/www/html/config/config.php -fi - -if [ -n "${S3MB:-}" ] -then - sed -i '/\/\/PLACEHOLDER/ r /etc/nc/s3mb.php' /var/www/html/config/config.php -fi - -if [ -n "${S3M:-}" ] -then - sed -i '/\/\/PLACEHOLDER/ r /etc/nc/s3m.php' /var/www/html/config/config.php -fi - -if [ -n "${SWIFT:-}" ] -then - sed -i '/\/\/PLACEHOLDER/ r /etc/nc/swift.php' /var/www/html/config/config.php -fi - -if [ -n "${SWIFTV3:-}" ] -then - sed -i '/\/\/PLACEHOLDER/ r /etc/nc/swiftv3.php' /var/www/html/config/config.php -fi - -if [ -n "${AZURE:-}" ] -then - sed -i '/\/\/PLACEHOLDER/ r /etc/nc/azure.php' /var/www/html/config/config.php -fi - -if [ -n "${REDIS_TLS:-}" ] -then - sed -i '/\/\/PLACEHOLDER/ r /etc/nc/redis-tls.php' /var/www/html/config/config.php -else - sed -i '/\/\/PLACEHOLDER/ r /etc/nc/redis-default.php' /var/www/html/config/config.php -fi +if ("REDIS_TLS" in $env) { + loadExtraConfig "redis-tls" +} else { + loadExtraConfig "redis-default" +} diff --git a/nix/image/scripts/occ b/nix/image/scripts/occ index cf7d6fa..07a443b 100755 --- a/nix/image/scripts/occ +++ b/nix/image/scripts/occ @@ -1,5 +1,5 @@ -#!/bin/sh +#!/bin/nu -export XDEBUG_SESSION=haze - -php $WEBROOT/occ "$@" +def --wrapped main [...rest] { + XDEBUG_SESSION=haze php $"($env.WEBROOT)/occ" ...$rest +} diff --git a/nix/image/scripts/tests b/nix/image/scripts/tests index 70691c0..524172c 100755 --- a/nix/image/scripts/tests +++ b/nix/image/scripts/tests @@ -1,7 +1,7 @@ -#!/bin/sh +#!/bin/nu -cd $WEBROOT +def main [...rest] { + cd $env.WEBROOT -export XDEBUG_SESSION=haze - -phpunit --configuration $WEBROOT/tests/phpunit-autotest.xml $@ + XDEBUG_SESSION=haze phpunit --configuration $"($env.WEBROOT)/tests/phpunit-autotest.xml" ...$rest +} From a1ed0571be4594da4381833e5b0e1659f78d884f Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Mon, 13 Apr 2026 20:59:20 +0200 Subject: [PATCH 45/68] exclude frankenphp from older php versions --- nix/image/haze.nix | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/nix/image/haze.nix b/nix/image/haze.nix index 8c7551e..5ce1051 100644 --- a/nix/image/haze.nix +++ b/nix/image/haze.nix @@ -37,8 +37,8 @@ frankenphp, nushell, }: let - inherit (builtins) toString; - inherit (lib) readFile getExe concatStringsSep splitString take; + inherit (builtins) toString compareVersions; + inherit (lib) readFile getExe concatStringsSep splitString take optionals; version = (fromTOML (readFile ../../Cargo.toml)).package.version; @@ -150,17 +150,20 @@ tag = phpVersion; fromImage = baseImage; - copyToRoot = [ - phpEnv - phpEnv.packages.composer - phpunit - (frankenphp.override { - php = php.withExtensions (import ./php-ext.nix { - inherit lib php; - enableBlackfire = false; - }); - }) - ]; + copyToRoot = + [ + phpEnv + phpEnv.packages.composer + phpunit + ] + ++ optionals ((compareVersions phpVersion "8.2") == 1) [ + (frankenphp.override { + php = php.withExtensions (import ./php-ext.nix { + inherit lib php; + enableBlackfire = false; + }); + }) + ]; }; in dockerTools.buildLayeredImage { From 814a1c3121d57a84dee4506cdbe247e52da8c0ee Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Mon, 13 Apr 2026 23:23:05 +0200 Subject: [PATCH 46/68] 2.2.1 --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index aba8178..22fafea 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -932,7 +932,7 @@ checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" [[package]] name = "haze" -version = "2.2.0" +version = "2.2.1" dependencies = [ "async-trait", "atty", diff --git a/Cargo.toml b/Cargo.toml index 73a4c8d..9318714 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "haze" -version = "2.2.0" +version = "2.2.1" authors = ["Robin Appelman "] edition = "2021" repository = "https://codeberg.org/icewind/haze" From 24b8fd26cac1d20b1cbb5785f5bb2f60017245f0 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Mon, 13 Apr 2026 23:28:56 +0200 Subject: [PATCH 47/68] rebuild images on version number change --- .forgejo/workflows/docker.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.forgejo/workflows/docker.yaml b/.forgejo/workflows/docker.yaml index c2dbdbd..a8c493d 100644 --- a/.forgejo/workflows/docker.yaml +++ b/.forgejo/workflows/docker.yaml @@ -4,6 +4,7 @@ on: push: branches: ["main"] paths: + - "Cargo.toml" - ".forgejo/workflows/docker.yaml" - "nix/image/**" From 96b7dd671c35a24b6a187393facde27e196888a7 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Mon, 13 Apr 2026 23:42:02 +0200 Subject: [PATCH 48/68] clippy fixes --- src/service/office.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/service/office.rs b/src/service/office.rs index 9977f57..8aaf99a 100644 --- a/src/service/office.rs +++ b/src/service/office.rs @@ -33,7 +33,7 @@ impl ServiceTrait for Office { _options: &CloudOptions, ) -> Result> { let network_info = docker - .inspect_network(&network, None) + .inspect_network(network, None) .await .into_diagnostic()?; let gateway = network_info @@ -65,7 +65,7 @@ impl ServiceTrait for Office { env.push(format!("server_name={clean_id}.{}", config.proxy.address)); } - let clean_cloud_id = cloud_id.strip_prefix("haze-").unwrap_or(&cloud_id); + let clean_cloud_id = cloud_id.strip_prefix("haze-").unwrap_or(cloud_id); let hosts = if proxy_base.is_empty() { vec![] } else { @@ -77,7 +77,7 @@ impl ServiceTrait for Office { let config = ContainerCreateBody { image: Some(image.into()), - env: Some(env.into_iter().map(String::from).collect()), + env: Some(env), host_config: Some(HostConfig { network_mode: Some(network.to_string()), extra_hosts: Some(hosts), From 19e60217ea3788be546bdb6cb3102491fc45a58b Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Tue, 14 Apr 2026 14:04:44 +0200 Subject: [PATCH 49/68] 127.0.0.1 now works for federation --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 839e91c..9234589 100644 --- a/README.md +++ b/README.md @@ -274,8 +274,7 @@ proxy to allow using a wildcard domain. ### Setup - Set a DNS record for `*.haze.exmaple.com` and `haze.example.com` pointing to - your development machine. Pointing it to `127.0.0.1` will also work, but comes - with limitations like federation not being supported. + your development machine. - Set the `proxy` configuration with your domain and desired listen endpoint. - Set up a service to run `haze proxy` in the background as your own user. A systemd user service is recommended (see [haze.service](./haze.service) for an From 948d01600ebe7881db61f4bacd356ec8ff820ece Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Tue, 14 Apr 2026 14:52:41 +0200 Subject: [PATCH 50/68] readme typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9234589..d284ea7 100644 --- a/README.md +++ b/README.md @@ -295,7 +295,7 @@ will automatically point to the last created instance. Additionally, the proxy allows access to the server containers trough either `-.haze.example.com` for a specific instance, or -`.haze.example.com` for the last created instead. For example +`.haze.example.com` for the last created instance. For example `rolling-bees-mail.haze.example.com` will give access to the smtp4dev web interface of the `rolling-bees` instance. From 53e30a94aa16e41f406f743c0f160ea50484a2be Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Wed, 15 Apr 2026 23:28:11 +0200 Subject: [PATCH 51/68] fix generated urls in integration tests --- nix/image/scripts/integration | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/nix/image/scripts/integration b/nix/image/scripts/integration index 8477132..32a9708 100755 --- a/nix/image/scripts/integration +++ b/nix/image/scripts/integration @@ -13,6 +13,10 @@ def main [feature: path, ...rest] { $feature = $feature | str replace "build/integration/" "" } + # don't use the proxy urls for generated urls + occ config:system:delete overwritehost + occ config:system:delete overwriteprotocol + cd $workdir bash run.sh $feature ...$rest } From 9de626a9058be9be25e65342ee0d021465558dad Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Thu, 16 Apr 2026 16:30:04 +0200 Subject: [PATCH 52/68] typo --- src/sources.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sources.rs b/src/sources.rs index b99c54a..ccfe564 100644 --- a/src/sources.rs +++ b/src/sources.rs @@ -122,7 +122,7 @@ pub async fn download_nc(config: &HazeConfig, version: &str) -> Result Date: Fri, 17 Apr 2026 22:22:24 +0200 Subject: [PATCH 53/68] autosetup for ldap fixes #19 --- src/service/ldap.rs | 45 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 42 insertions(+), 3 deletions(-) diff --git a/src/service/ldap.rs b/src/service/ldap.rs index aa082b0..22b7383 100644 --- a/src/service/ldap.rs +++ b/src/service/ldap.rs @@ -86,6 +86,47 @@ impl ServiceTrait for Ldap { ) -> Result { self.is_running(docker, cloud_id).await } + + async fn post_setup( + &self, + _docker: &Docker, + _cloud_id: &str, + _config: &HazeConfig, + ) -> Result> { + Ok(vec![ + "occ ldap:create-empty-config".into(), + "occ ldap:set-config s01 ldapHost 'ldap://ldap'".into(), + "occ ldap:set-config s01 ldapPort '389'".into(), + "occ ldap:set-config s01 ldapAgentName 'cn=admin,dc=example,dc=org'".into(), + "occ ldap:set-config s01 ldapAgentPassword 'haze'".into(), + "occ ldap:set-config s01 ldapBase 'dc=example,dc=org'".into(), + "occ ldap:set-config s01 ldapBaseUsers 'dc=example,dc=org'".into(), + "occ ldap:set-config s01 ldapBaseGroups 'dc=example,dc=org'".into(), + "occ ldap:set-config s01 ldapLoginFilter '(&(&(objectclass=inetOrgPerson))(uid=%uid))'" + .into(), + "occ ldap:set-config s01 ldapUserFilter '((objectclass=inetOrgPerson))'".into(), + "occ ldap:set-config s01 ldapUserFilterMode '0'".into(), + "occ ldap:set-config s01 ldapUserDisplayName 'sn'".into(), + "occ ldap:set-config s01 ldapUserFilterObjectclass 'inetOrgPerson'".into(), + "occ ldap:set-config s01 ldapGroupFilter '(&(|(objectclass=posixGroup)))'".into(), + "occ ldap:set-config s01 ldapGroupFilterObjectclass 'posixGroup'".into(), + "occ ldap:set-config s01 ldapEmailAttribute 'email'".into(), + "occ ldap:set-config s01 ldapUuidUserAttribute 'email'".into(), + "occ ldap:set-config s01 ldapUuidUserAttribute 'auto'".into(), + "occ ldap:set-config s01 ldapUuidGroupAttribute 'auto'".into(), + "occ ldap:set-config s01 ldapLoginFilterUsername '1'".into(), + "occ ldap:set-config s01 ldapConfigurationActive '1'".into(), + ]) + } + + async fn start_message( + &self, + _docker: &Docker, + _cloud_id: &str, + _proxy: &ProxyConfig, + ) -> Result> { + Ok(Some(format!("\nLdap users provisioned:\n\t'cn=admin,dc=example,dc=org' and password 'haze'\n\t'cn=ldaptest,dc=example,dc=org' and password 'test'\n\nldaptest is available for login\n"))) + } } #[derive(Debug, Clone, Eq, PartialEq)] @@ -185,8 +226,6 @@ impl ServiceTrait for LdapAdmin { return Err(Report::msg("ldap admin not started")); }; let addr = proxy.addr(&id, IpAddr::from_str(&ip).unwrap()); - Ok(Some(format!( - "Ldap admin running at: {addr} with 'cn=admin,dc=example,dc=org' and password 'haze'" - ))) + Ok(Some(format!("Ldap admin running at: {addr}"))) } } From b4a77997ab31836cdf1eb47298e0ba10afacc415 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Tue, 28 Apr 2026 18:54:44 +0200 Subject: [PATCH 54/68] fpm status page --- nix/image/configs/nginx.conf | 11 +++++++++++ nix/image/configs/php-fpm.conf | 1 + 2 files changed, 12 insertions(+) diff --git a/nix/image/configs/nginx.conf b/nix/image/configs/nginx.conf index b25ab52..15a57c3 100644 --- a/nix/image/configs/nginx.conf +++ b/nix/image/configs/nginx.conf @@ -109,5 +109,16 @@ http { expires 7d; # Cache-Control policy borrowed from `.htaccess` access_log off; # Optional: Don't log access to assets } + + location /fpm-status { + include /conf/fastcgi_params; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + fastcgi_param PATH_INFO $fastcgi_path_info; + fastcgi_pass php-handler; + fastcgi_read_timeout 3600; + proxy_request_buffering off; + fastcgi_request_buffering off; + fastcgi_buffering off; + } } } diff --git a/nix/image/configs/php-fpm.conf b/nix/image/configs/php-fpm.conf index e6292fb..c7751a4 100644 --- a/nix/image/configs/php-fpm.conf +++ b/nix/image/configs/php-fpm.conf @@ -21,6 +21,7 @@ pm.max_children = 5 pm.start_servers = 2 pm.min_spare_servers = 1 pm.max_spare_servers = 3 +pm.status_path = /fpm-status clear_env = no From ea3f89bb040f2f2fc8d089768eb450f071dff0c8 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Tue, 28 Apr 2026 18:55:08 +0200 Subject: [PATCH 55/68] fpm logs --- nix/image/configs/php-fpm.conf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nix/image/configs/php-fpm.conf b/nix/image/configs/php-fpm.conf index c7751a4..89379d8 100644 --- a/nix/image/configs/php-fpm.conf +++ b/nix/image/configs/php-fpm.conf @@ -1,11 +1,11 @@ [global] -error_log = /proc/self/fd/2 +error_log = /var/log/php-fpm-error.log daemonize = no [www] -access.log = /proc/self/fd/2 +access.log =/var/log/php-fpm-access.log user = haze group = haze From 512b669a7c02d4d5798b8579731fd7009d68d86c Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Thu, 30 Apr 2026 19:08:10 +0200 Subject: [PATCH 56/68] sqlite table mode --- nix/image/bootstrap | 1 + nix/image/configs/home/.sqliterc | 1 + 2 files changed, 2 insertions(+) create mode 100644 nix/image/configs/home/.sqliterc diff --git a/nix/image/bootstrap b/nix/image/bootstrap index d16e605..2c9dcff 100755 --- a/nix/image/bootstrap +++ b/nix/image/bootstrap @@ -42,6 +42,7 @@ if ((getent group $HAZE_GID | length) > 0) { useradd -u $HAZE_UID -g $HAZE_GID haze } chown -R $"haze:($HAZE_GID)" /home/haze +ls -af /etc/home | each {|file| ln -s $file.name $"/home/haze/($file.name | path basename)" } if ("/var/run/docker.sock" | path exists) { let dockerGid = stat --format "%g" /var/run/docker.sock diff --git a/nix/image/configs/home/.sqliterc b/nix/image/configs/home/.sqliterc new file mode 100644 index 0000000..0d5fe04 --- /dev/null +++ b/nix/image/configs/home/.sqliterc @@ -0,0 +1 @@ +.mode table From 9e080f0d544da3950337baf966068cc08246c0e1 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Thu, 7 May 2026 18:40:17 +0200 Subject: [PATCH 57/68] fix using '--' flags in test and integration commands --- nix/image/scripts/integration | 2 +- nix/image/scripts/tests | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/nix/image/scripts/integration b/nix/image/scripts/integration index 32a9708..d8187d9 100755 --- a/nix/image/scripts/integration +++ b/nix/image/scripts/integration @@ -1,6 +1,6 @@ #!/bin/nu -def main [feature: path, ...rest] { +def --wrapped main [feature: path, ...rest] { mut feature = $feature; mut workdir = $"($env.WEBROOT)/build/integration" if ($feature | str starts-with "apps/") { diff --git a/nix/image/scripts/tests b/nix/image/scripts/tests index 524172c..868f225 100755 --- a/nix/image/scripts/tests +++ b/nix/image/scripts/tests @@ -1,6 +1,6 @@ #!/bin/nu -def main [...rest] { +def --wrapped main [...rest] { cd $env.WEBROOT XDEBUG_SESSION=haze phpunit --configuration $"($env.WEBROOT)/tests/phpunit-autotest.xml" ...$rest From 373ce0f3fd63cd782efd881d0c1e18b12eb1baec Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Thu, 7 May 2026 18:58:54 +0200 Subject: [PATCH 58/68] add php-imagick fixes #22 --- nix/image/php-ext.nix | 1 + nix/image/php.nix | 38 +++++++++++++++++--------------------- 2 files changed, 18 insertions(+), 21 deletions(-) diff --git a/nix/image/php-ext.nix b/nix/image/php-ext.nix index 2715cf2..69d23e1 100644 --- a/nix/image/php-ext.nix +++ b/nix/image/php-ext.nix @@ -35,6 +35,7 @@ in gmp apcu ffi + imagick ] ++ optionals (!debug) [ smbclient # this breaks the build for no apparent reason diff --git a/nix/image/php.nix b/nix/image/php.nix index 4922655..8ef3d31 100644 --- a/nix/image/php.nix +++ b/nix/image/php.nix @@ -2,27 +2,23 @@ lib, php, debug ? false, -}: let - inherit (builtins) compareVersions; - inherit (lib) optionals; - withBlackfire = !debug && ((compareVersions php.version "8.1.0") == 1); -in - php.buildEnv { - extensions = import ./php-ext.nix {inherit lib php debug;}; - extraConfig = '' - xdebug.mode=debug,trace,profile - xdebug.start_with_request=trigger - xdebug.discover_client_host=false - xdebug.client_host=hazehost - xdebug.log_level=0 - xdebug.output_dir=/tmp/xdebug +}: +php.buildEnv { + extensions = import ./php-ext.nix {inherit lib php debug;}; + extraConfig = '' + xdebug.mode=debug,trace,profile + xdebug.start_with_request=trigger + xdebug.discover_client_host=false + xdebug.client_host=hazehost + xdebug.log_level=0 + xdebug.output_dir=/tmp/xdebug - memory_limit=512M + memory_limit=512M - post_max_size 10G - upload_max_filesize 10G + post_max_size 10G + upload_max_filesize 10G - apc.enable_cli=1 - opcache.enable_cli=1 - ''; - } + apc.enable_cli=1 + opcache.enable_cli=1 + ''; +} From 8771e7dc5f3f02b152c403575d3152722f701a67 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Fri, 8 May 2026 18:57:19 +0200 Subject: [PATCH 59/68] switch to multiprogress for git pull --- src/git.rs | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/src/git.rs b/src/git.rs index 3a797c3..c20d450 100644 --- a/src/git.rs +++ b/src/git.rs @@ -2,11 +2,13 @@ use crate::config::HazeConfig; use crate::Result; use git2::build::CheckoutBuilder; use git2::{Branch, BranchType, Repository, RepositoryState}; +use indicatif::{MultiProgress, ProgressBar, ProgressStyle}; use miette::{Context, IntoDiagnostic}; use std::fs::read_dir; use std::iter::once; use std::path::PathBuf; use std::process::Command; +use std::time::Duration; fn find_app_repos(config: &HazeConfig) -> Result> { let apps_dirs = once(config.sources_root.as_path().join("apps")) @@ -83,6 +85,9 @@ const GIT_BINARY: &str = match option_env!("GIT_BINARY") { pub fn pull_all(config: &HazeConfig) -> Result<()> { let (max_app, max_branch) = longest_app_branch(config)?; + let progress = MultiProgress::new(); + let pull_style = ProgressStyle::with_template("{spinner:.green} {msg}").unwrap(); + for app_dir in find_app_repos(config)? { let app_name = app_dir.file_name().unwrap().to_string_lossy(); let repo = Repository::init(&app_dir) @@ -90,17 +95,26 @@ pub fn pull_all(config: &HazeConfig) -> Result<()> { .wrap_err_with(|| format!("Failed to open repository {}", app_dir.display()))?; let branch_name = current_branch_name(&repo).unwrap_or("unknown".into()); - print!( - "{app_name: Result<()> { .wrap_err_with(|| format!("Failed to run git pull for {}", app_dir.display()))?; if output.status.success() { - println!(" ✓"); + bar.set_message(msg(" ✓")); } else { - println!(" ❌"); - eprintln!("{}", String::from_utf8_lossy(&output.stderr)) + let err = String::from_utf8_lossy(&output.stderr); + let err_line = err.lines().next().unwrap(); + bar.set_message(msg(&format!(" ❌ {err_line}"))); } + bar.finish(); } Ok(()) } From b977cd9dfa900324e90ffb496b42ac4b5bb1b799 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Fri, 8 May 2026 19:09:31 +0200 Subject: [PATCH 60/68] parallelize git pull fixes #21 --- Cargo.lock | 46 +++++++++++++++++++++++++++ Cargo.toml | 1 + src/git.rs | 92 ++++++++++++++++++++++++++++++++---------------------- 3 files changed, 101 insertions(+), 38 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 22fafea..90f1ad9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -506,6 +506,31 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + [[package]] name = "crypto-common" version = "0.1.7" @@ -956,6 +981,7 @@ dependencies = [ "opener", "owo-colors", "petname", + "rayon", "reqwest", "serde", "serde_json", @@ -2002,6 +2028,26 @@ dependencies = [ "getrandom 0.3.4", ] +[[package]] +name = "rayon" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb39b166781f92d482534ef4b4b1b2568f42613b53e5b6c160e24cfbfa30926d" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + [[package]] name = "redox_syscall" version = "0.5.10" diff --git a/Cargo.toml b/Cargo.toml index 9318714..947de7c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,6 +39,7 @@ zip = "8.1.0" sha2 = "0.11.0-rc.5" base16ct = { version = "1.0.0", features = ["alloc"] } indicatif = "0.18.4" +rayon = "1.12.0" hyper-reverse-proxy = { version = "0.5.2-dev", git = "https://github.com/chpio/hyper-reverse-proxy", rev = "6934877eb74465204f605cc1c05ca5a9772db7c0" } hyper = "1.8.1" diff --git a/src/git.rs b/src/git.rs index c20d450..50ebb05 100644 --- a/src/git.rs +++ b/src/git.rs @@ -4,6 +4,8 @@ use git2::build::CheckoutBuilder; use git2::{Branch, BranchType, Repository, RepositoryState}; use indicatif::{MultiProgress, ProgressBar, ProgressStyle}; use miette::{Context, IntoDiagnostic}; +use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; +use rayon::ThreadPoolBuilder; use std::fs::read_dir; use std::iter::once; use std::path::PathBuf; @@ -88,49 +90,63 @@ pub fn pull_all(config: &HazeConfig) -> Result<()> { let progress = MultiProgress::new(); let pull_style = ProgressStyle::with_template("{spinner:.green} {msg}").unwrap(); - for app_dir in find_app_repos(config)? { - let app_name = app_dir.file_name().unwrap().to_string_lossy(); - let repo = Repository::init(&app_dir) - .into_diagnostic() - .wrap_err_with(|| format!("Failed to open repository {}", app_dir.display()))?; - let branch_name = current_branch_name(&repo).unwrap_or("unknown".into()); + let pool = ThreadPoolBuilder::new() + .num_threads(8) + .build() + .into_diagnostic()?; + let repos = find_app_repos(config)?.collect::>(); - let bar = ProgressBar::new_spinner().with_style(pull_style.clone()); - bar.enable_steady_tick(Duration::from_millis(100)); - let bar = progress.add(bar); + pool.install(|| { + repos.par_iter().for_each(|app_dir| { + let app_name = app_dir.file_name().unwrap().to_string_lossy(); + let Ok(repo) = Repository::init(&app_dir) else { + return; + }; + let branch_name = current_branch_name(&repo).unwrap_or("unknown".into()); - let msg = |state: &str| { - format!( - "{app_name: output, + Err(error) => { + bar.set_message(msg(&format!(" ⨯ {error}"))); + return; + } + }; + + if output.status.success() { + bar.set_message(msg(" ✓")); + } else { + let err = String::from_utf8_lossy(&output.stderr); + let err_line = err.lines().next().unwrap(); + bar.set_message(msg(&format!(" ⨯ {err_line}"))); + } bar.finish(); - continue; - } + }); + }); - bar.set_message(msg("")); - - let output = Command::new(GIT_BINARY) - .arg("pull") - .current_dir(&app_dir) - .output() - .into_diagnostic() - .wrap_err_with(|| format!("Failed to run git pull for {}", app_dir.display()))?; - - if output.status.success() { - bar.set_message(msg(" ✓")); - } else { - let err = String::from_utf8_lossy(&output.stderr); - let err_line = err.lines().next().unwrap(); - bar.set_message(msg(&format!(" ❌ {err_line}"))); - } - bar.finish(); - } Ok(()) } From ad999702aabb605c73c597fca95b992f14f1ac54 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Fri, 8 May 2026 20:24:17 +0200 Subject: [PATCH 61/68] improve websocket proxying --- Cargo.lock | 49 +++++++++++++++------------- Cargo.toml | 7 ++-- nix/package.nix | 2 +- src/proxy.rs | 72 ++++++++++++++++++++++++++++++++---------- src/service.rs | 6 ++++ src/service/webhook.rs | 71 +++++++++++++++++++++++++++++++++++++++++ 6 files changed, 164 insertions(+), 43 deletions(-) create mode 100644 src/service/webhook.rs diff --git a/Cargo.lock b/Cargo.lock index 90f1ad9..ccedb17 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -147,6 +147,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b52af3cb4058c895d37317bb27508dccc8e5f2d39454016b297bf4a400597b8" dependencies = [ "axum-core", + "axum-macros", "bytes", "form_urlencoded", "futures-util", @@ -192,6 +193,17 @@ dependencies = [ "tracing", ] +[[package]] +name = "axum-macros" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "604fde5e028fea851ce1d8570bbdc034bec850d157f7569d10f347d06808c05c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "backtrace" version = "0.3.74" @@ -921,25 +933,6 @@ dependencies = [ "url", ] -[[package]] -name = "h2" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5017294ff4bb30944501348f6f8e42e6ad28f42c8bbef7a74029aff064a4e3c2" -dependencies = [ - "atomic-waker", - "bytes", - "fnv", - "futures-core", - "futures-sink", - "http", - "indexmap", - "slab", - "tokio", - "tokio-util", - "tracing", -] - [[package]] name = "hashbrown" version = "0.15.2" @@ -957,7 +950,7 @@ checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" [[package]] name = "haze" -version = "2.2.1" +version = "2.2.2" dependencies = [ "async-trait", "atty", @@ -991,6 +984,7 @@ dependencies = [ "tar", "termion", "tokio", + "tokio-stream", "toml", "tracing", "tracing-subscriber", @@ -1098,7 +1092,6 @@ dependencies = [ "bytes", "futures-channel", "futures-core", - "h2", "http", "http-body", "httparse", @@ -1129,8 +1122,9 @@ dependencies = [ [[package]] name = "hyper-reverse-proxy" version = "0.5.2-dev" -source = "git+https://github.com/chpio/hyper-reverse-proxy?rev=6934877eb74465204f605cc1c05ca5a9772db7c0#6934877eb74465204f605cc1c05ca5a9772db7c0" +source = "git+https://code.betamike.com/micropelago/hyper-reverse-proxy.git?rev=d5a6f799189360d9449ae47ab3cdde511f02cf39#d5a6f799189360d9449ae47ab3cdde511f02cf39" dependencies = [ + "http-body-util", "hyper", "hyper-util", "tokio", @@ -2741,6 +2735,17 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-stream" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32da49809aab5c3bc678af03902d4ccddea2a87d028d86392a4b1560c6906c70" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + [[package]] name = "tokio-util" version = "0.7.13" diff --git a/Cargo.toml b/Cargo.toml index 947de7c..6da76a8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "haze" -version = "2.2.1" +version = "2.2.2" authors = ["Robin Appelman "] edition = "2021" repository = "https://codeberg.org/icewind/haze" @@ -12,6 +12,7 @@ bollard = "0.20.1" maplit = "1.0.2" camino = { version = "1.2.2", features = ["serde1"] } tokio = { version = "1.49.0", features = ["fs", "macros", "signal", "rt-multi-thread"] } +tokio-stream = { version = "0.1.18", features = ["net"] } futures-util = "0.3.32" termion = "4.0.6" opener = "0.8.4" @@ -41,10 +42,10 @@ base16ct = { version = "1.0.0", features = ["alloc"] } indicatif = "0.18.4" rayon = "1.12.0" -hyper-reverse-proxy = { version = "0.5.2-dev", git = "https://github.com/chpio/hyper-reverse-proxy", rev = "6934877eb74465204f605cc1c05ca5a9772db7c0" } +hyper-reverse-proxy = { version = "0.5.2-dev", git = "https://code.betamike.com/micropelago/hyper-reverse-proxy.git", rev = "d5a6f799189360d9449ae47ab3cdde511f02cf39" } hyper = "1.8.1" hyper-util = "0.1.20" -axum = { version = "0.8.8", features = ["tokio"] } +axum = { version = "0.8.8", features = ["tokio", "macros"] } [profile.release] lto = true diff --git a/nix/package.nix b/nix/package.nix index fcd6489..34a4388 100644 --- a/nix/package.nix +++ b/nix/package.nix @@ -20,7 +20,7 @@ in cargoLock = { lockFile = ../Cargo.lock; outputHashes = { - "hyper-reverse-proxy-0.5.2-dev" = "sha256-+ebi4FVVkiOpf75e8K5oGkHJBYQjLNJhUPNj+78zd7Q="; + "hyper-reverse-proxy-0.5.2-dev" = "sha256-awmj5aLFTea+kj81cwmfP1HWlWezwEKfyQSUJWjtamk="; }; }; } diff --git a/src/proxy.rs b/src/proxy.rs index d5a3b0e..4784a18 100644 --- a/src/proxy.rs +++ b/src/proxy.rs @@ -5,26 +5,34 @@ use axum::http::header::HOST; use axum::http::HeaderValue; use axum::{ body::Body, - extract::{Request, State}, + extract::Request, response::{IntoResponse, Response}, - Router, }; use bollard::Docker; +use futures_util::StreamExt; +use hyper::body::Incoming; +use hyper::server::conn::http1; +use hyper::service::service_fn; use hyper::StatusCode; +use hyper_util::rt::TokioIo; use hyper_util::{client::legacy::connect::HttpConnector, rt::TokioExecutor}; use miette::{miette, IntoDiagnostic}; use std::collections::HashMap; +use std::convert::Infallible; use std::fs::{create_dir_all, set_permissions}; use std::net::{IpAddr, Ipv4Addr, SocketAddr}; use std::os::unix::fs::PermissionsExt; use std::path::PathBuf; +use std::pin::pin; use std::str::FromStr; use std::sync::{Arc, Mutex}; use std::time::Duration; +use tokio::io::{AsyncRead, AsyncWrite}; use tokio::net::UnixListener; use tokio::signal::ctrl_c; use tokio::spawn; use tokio::time::sleep; +use tokio_stream::wrappers::{TcpListenerStream, UnixListenerStream}; use tracing::{debug, error, info}; struct ActiveInstances { @@ -163,20 +171,26 @@ async fn serve(instances: ActiveInstances, listen: String, base_address: String) ctrl_c().await.ok(); }; - let app = Router::new().fallback(handler).with_state(AppState { + let state = AppState { instances: instances.clone(), base_address: base_address.clone(), proxy_client: Arc::new(proxy_client), - }); + }; if !listen.starts_with('/') { let addr: SocketAddr = listen.parse().into_diagnostic()?; let listener = tokio::net::TcpListener::bind(addr).await.unwrap(); println!("listening on {}", listener.local_addr().unwrap()); - axum::serve(listener, app) - .with_graceful_shutdown(cancel) - .await - .unwrap(); + let mut connections = pin!(TcpListenerStream::new(listener).take_until(cancel)); + + while let Some(stream) = connections.next().await { + match stream { + Ok(stream) => handle_connection(state.clone(), stream), + Err(error) => { + error!(%error, "connection failed"); + } + } + } } else { let listen: PathBuf = listen.into(); if let Some(parent) = listen.parent() { @@ -187,18 +201,42 @@ async fn serve(instances: ActiveInstances, listen: String, base_address: String) } let _ = tokio::fs::remove_file(&listen).await; - let uds = UnixListener::bind(&listen).unwrap(); + let listener = UnixListener::bind(&listen).unwrap(); + println!("listening on {}", listen.display()); set_permissions(&listen, PermissionsExt::from_mode(0o666)).into_diagnostic()?; - axum::serve(uds, app) - .with_graceful_shutdown(cancel) - .await - .unwrap(); + let mut connections = pin!(UnixListenerStream::new(listener).take_until(cancel)); + + while let Some(stream) = connections.next().await { + match stream { + Ok(stream) => handle_connection(state.clone(), stream), + Err(error) => { + error!(%error, "connection failed"); + } + } + } } Ok(()) } +fn handle_connection( + state: AppState, + stream: I, +) { + let io = TokioIo::new(stream); + // Spawn a tokio task to serve multiple connections concurrently + tokio::task::spawn(async move { + if let Err(err) = http1::Builder::new() + .serve_connection(io, service_fn(move |req| handler(state.clone(), req))) + .with_upgrades() + .await + { + eprintln!("Error serving connection: {:?}", err); + } + }); +} + async fn get_remote( host: Option<&HeaderValue>, instances: &ActiveInstances, @@ -232,9 +270,9 @@ async fn get_remote( } } -type Client = hyper_util::client::legacy::Client; +type Client = hyper_util::client::legacy::Client; -async fn handler(State(state): State, mut req: Request) -> Result { +async fn handler(state: AppState, mut req: Request) -> Result { let host = req.headers().get(HOST).cloned(); let remote = match get_remote(host.as_ref(), &state.instances, &state.base_address).await { Ok(remote) => remote, @@ -259,13 +297,13 @@ async fn handler(State(state): State, mut req: Request) -> Result Ok(response.map(Body::new)), Err(error) => { - error!(%error, "error while proxying request"); + error!(?error, "error while proxying request"); Ok(StatusCode::BAD_REQUEST.into_response()) } } diff --git a/src/service.rs b/src/service.rs index 49bb636..490f617 100644 --- a/src/service.rs +++ b/src/service.rs @@ -14,6 +14,7 @@ mod sftp; mod redis; mod sharded; mod smb; +mod webhook; use crate::cloud::CloudOptions; use crate::config::{HazeConfig, Preset, ProxyConfig}; @@ -32,6 +33,7 @@ use crate::service::redis::Redis; use crate::service::sftp::Sftp; use crate::service::sharded::{Sharding, ShardingMigrate, ShardingMigrateUnset, SingleShard}; use crate::service::smb::Smb; +use crate::service::webhook::Webhook; use bollard::models::ContainerState; use bollard::Docker; use enum_dispatch::enum_dispatch; @@ -296,6 +298,8 @@ pub enum ServiceType { RedisTls, /// Use FrankenPHP instead of PHP-FPM FrankenPhp, + /// Webhook test listener + Webhook, } #[enum_dispatch] @@ -326,6 +330,7 @@ pub enum Service { Redis(Redis), RedisTls(RedisTls), FrankenPhp(FrankenPhp), + Webhook(Webhook), Preset(PresetService), } @@ -369,6 +374,7 @@ impl Service { ServiceType::Redis => Some(vec![Service::Redis(Redis)]), ServiceType::RedisTls => Some(vec![Service::RedisTls(RedisTls)]), ServiceType::FrankenPhp => Some(vec![Service::FrankenPhp(FrankenPhp)]), + ServiceType::Webhook => Some(vec![Service::Webhook(Webhook)]), } } else { presets diff --git a/src/service/webhook.rs b/src/service/webhook.rs new file mode 100644 index 0000000..3967483 --- /dev/null +++ b/src/service/webhook.rs @@ -0,0 +1,71 @@ +use crate::cloud::CloudOptions; +use crate::config::HazeConfig; +use crate::image::pull_image; +use crate::service::ServiceTrait; +use crate::Result; +use bollard::models::{ContainerCreateBody, EndpointSettings, HostConfig, NetworkingConfig}; +use bollard::query_parameters::CreateContainerOptions; +use bollard::Docker; +use maplit::hashmap; +use miette::IntoDiagnostic; + +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct Webhook; + +#[async_trait::async_trait] +impl ServiceTrait for Webhook { + fn name(&self) -> &str { + "webhook" + } + + async fn spawn( + &self, + docker: &Docker, + cloud_id: &str, + network: &str, + _config: &HazeConfig, + _options: &CloudOptions, + ) -> Result> { + let image = "ghcr.io/tarampampam/webhook-tester"; + pull_image(docker, image).await?; + let options = Some(CreateContainerOptions { + name: self.container_name(cloud_id), + ..CreateContainerOptions::default() + }); + let config = ContainerCreateBody { + image: Some(image.into()), + host_config: Some(HostConfig { + network_mode: Some(network.to_string()), + ..Default::default() + }), + labels: Some(hashmap! { + "haze-type".into() => self.name().into(), + "haze-cloud-id".into() => cloud_id.into(), + }), + networking_config: Some(NetworkingConfig { + endpoints_config: Some(hashmap! { + network.into() => EndpointSettings { + aliases: Some(vec![self.name().to_string()]), + ..Default::default() + } + }), + }), + ..Default::default() + }; + let id = docker + .create_container(options, config) + .await + .into_diagnostic()? + .id; + docker.start_container(&id, None).await.into_diagnostic()?; + Ok(vec![id]) + } + + fn container_name(&self, cloud_id: &str) -> Option { + Some(format!("{}-webhook", cloud_id)) + } + + fn proxy_port(&self) -> u16 { + 8080 + } +} From 39ba7a2a5309e18a583513e2efc7f688830bc75c Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Fri, 8 May 2026 20:33:48 +0200 Subject: [PATCH 62/68] mention webhook tester in readme --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index d284ea7..da8fecc 100644 --- a/README.md +++ b/README.md @@ -93,6 +93,8 @@ Additionally, you can use the following options when starting an instance: generation. - `mail`: start a [smtp4dev](https://github.com/rnwood/smtp4dev) server and configure it the mail server. +- `webhook` start a + [webhook tester](https://github.com/tarampampam/webhook-tester) - `redis`: start a separate container for redis. - `redis-tls`: connect to redis over TLS. - ``: by specifying the path to an app package this package From cd9740675f0c0de66b25c6d13303a286df9f0def Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Fri, 8 May 2026 22:23:40 +0200 Subject: [PATCH 63/68] clippy fixes --- src/git.rs | 4 ++-- src/service/ldap.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/git.rs b/src/git.rs index 50ebb05..fc31255 100644 --- a/src/git.rs +++ b/src/git.rs @@ -99,7 +99,7 @@ pub fn pull_all(config: &HazeConfig) -> Result<()> { pool.install(|| { repos.par_iter().for_each(|app_dir| { let app_name = app_dir.file_name().unwrap().to_string_lossy(); - let Ok(repo) = Repository::init(&app_dir) else { + let Ok(repo) = Repository::init(app_dir) else { return; }; let branch_name = current_branch_name(&repo).unwrap_or("unknown".into()); @@ -126,7 +126,7 @@ pub fn pull_all(config: &HazeConfig) -> Result<()> { let output = match Command::new(GIT_BINARY) .arg("pull") - .current_dir(&app_dir) + .current_dir(app_dir) .output() { Ok(output) => output, diff --git a/src/service/ldap.rs b/src/service/ldap.rs index 22b7383..bd41ba3 100644 --- a/src/service/ldap.rs +++ b/src/service/ldap.rs @@ -125,7 +125,7 @@ impl ServiceTrait for Ldap { _cloud_id: &str, _proxy: &ProxyConfig, ) -> Result> { - Ok(Some(format!("\nLdap users provisioned:\n\t'cn=admin,dc=example,dc=org' and password 'haze'\n\t'cn=ldaptest,dc=example,dc=org' and password 'test'\n\nldaptest is available for login\n"))) + Ok(Some("\nLdap users provisioned:\n\t'cn=admin,dc=example,dc=org' and password 'haze'\n\t'cn=ldaptest,dc=example,dc=org' and password 'test'\n\nldaptest is available for login\n".into())) } } From f99238121b0755e302c4620aaab65f90dd158a21 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Sat, 9 May 2026 16:45:52 +0200 Subject: [PATCH 64/68] nix cleanup --- nix/package.nix | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/nix/package.nix b/nix/package.nix index 34a4388..c6166f0 100644 --- a/nix/package.nix +++ b/nix/package.nix @@ -1,6 +1,5 @@ { rustPlatform, - pkg-config, lib, git, }: let @@ -10,7 +9,7 @@ src = sourceByRegex ../. ["Cargo.*" "(src|certificates)(/.*)?"]; version = (fromTOML (readFile ../Cargo.toml)).package.version; in - rustPlatform.buildRustPackage rec { + rustPlatform.buildRustPackage { pname = "haze"; inherit src version; From 204fb676d67aa0f794e070662310a79ae6e7e429 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Tue, 26 May 2026 20:45:29 +0200 Subject: [PATCH 65/68] add sftp with key authentication service --- README.md | 2 + certificates/sftp/id_rsa | 38 +++++++++++ certificates/sftp/id_rsa.pub | 1 + src/main.rs | 7 +- src/service.rs | 28 ++++++-- src/service/clam.rs | 46 ++++++------- src/service/dav.rs | 12 ++-- src/service/imaginary.rs | 13 ++-- src/service/kaspersky.rs | 24 +++---- src/service/ldap.rs | 47 +++++++------ src/service/mail.rs | 16 ++--- src/service/objectstore.rs | 22 +++---- src/service/oc.rs | 2 +- src/service/office.rs | 20 +++++- src/service/onlyoffice.rs | 46 ++++++++++--- src/service/push.rs | 16 +++-- src/service/redis.rs | 8 ++- src/service/sftp.rs | 123 ++++++++++++++++++++++++++++++++--- src/service/smb.rs | 14 ++-- 19 files changed, 349 insertions(+), 136 deletions(-) create mode 100644 certificates/sftp/id_rsa create mode 100644 certificates/sftp/id_rsa.pub diff --git a/README.md b/README.md index da8fecc..026c190 100644 --- a/README.md +++ b/README.md @@ -81,6 +81,8 @@ Additionally, you can use the following options when starting an instance: - `smb`: set up a samba server for external storage use. - `dav`: set up a WebDAV server for external storage use. - `sftp`: set up a SFTP server for external storage use. +- `sftp-key`: set up a SFTP server for external storage use with public key + authentication. - `kaspersky`: set up a kaspersky scan engine server in http mode. ( Requires [manually setting up the image](https://github.com/icewind1991/kaspersky-docker)) - `kaspersky-icap`: setup a kaspersky scan engine server in ICAP mode. diff --git a/certificates/sftp/id_rsa b/certificates/sftp/id_rsa new file mode 100644 index 0000000..c8dcc4d --- /dev/null +++ b/certificates/sftp/id_rsa @@ -0,0 +1,38 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABlwAAAAdzc2gtcn +NhAAAAAwEAAQAAAYEA323aWqH6YwRLbCBO94UKOkfnJ2m6Zsic0dMt3TmDnjLU0JzpOt7w +t5+mMZrEKQTpefozyUHo3z+HkmllLAGOupNy3A+jG2O955UUgw0dGfu6j6OOb66Du9jpqt +8BQ6gr3cEYASplPI7B889/cVpJ5l1HiBUgyR7Z16v15qCDtmpFVIECAdICEmPosfmZutt3 +YYl9xLay5WCmUztWS/amPcGs0DOEGrWeCtdxGKWT3TywdBKyQ0PbdYMamgDIT7JV1ZSzZP +aly4sB7E+dpS5AgBFVXmZ61151KN1TJ8gyoUjFhY7ctYEIpncZmyT4PYvyIvxRsbJtvERi +eNH8DoX5DwtqcxbgHK0OwYtdl4ydRXToYo3l+qIidf+g8ADVea/mbkfTPegdToo3LOuThX +OwExDlukpM8obFDpz1Yl1L6rRJAVNO1KmHWhn6to23jtYjBhczA2nkemQXQbVSjc/hItjQ +DIFNMOsLW33P+Y2k9LkpI0TL09ogOxOFZzGZp2tNAAAFgIgMIZ+IDCGfAAAAB3NzaC1yc2 +EAAAGBAN9t2lqh+mMES2wgTveFCjpH5ydpumbInNHTLd05g54y1NCc6Tre8LefpjGaxCkE +6Xn6M8lB6N8/h5JpZSwBjrqTctwPoxtjveeVFIMNHRn7uo+jjm+ug7vY6arfAUOoK93BGA +EqZTyOwfPPf3FaSeZdR4gVIMke2der9eagg7ZqRVSBAgHSAhJj6LH5mbrbd2GJfcS2suVg +plM7Vkv2pj3BrNAzhBq1ngrXcRilk908sHQSskND23WDGpoAyE+yVdWUs2T2pcuLAexPna +UuQIARVV5metdedSjdUyfIMqFIxYWO3LWBCKZ3GZsk+D2L8iL8UbGybbxEYnjR/A6F+Q8L +anMW4BytDsGLXZeMnUV06GKN5fqiInX/oPAA1Xmv5m5H0z3oHU6KNyzrk4VzsBMQ5bpKTP +KGxQ6c9WJdS+q0SQFTTtSph1oZ+raNt47WIwYXMwNp5HpkF0G1Uo3P4SLY0AyBTTDrC1t9 +z/mNpPS5KSNEy9PaIDsThWcxmadrTQAAAAMBAAEAAAGAWCkM/TEnztU9e3M+JX253OhNRe +h6lB75ffOxh7avgAc3oP8hKkkYu6PDnJQgbb0R8T7wGywmGp0DPhrXQGd27ZjLvBhxeBfB +sbTJ7LIKdxu0cAQN6nR2Z3M+NF2dLpiXgn80HRWg76W20yDffRcuzLamyIPptWI2e9rPAw +r4HczOAXuMErLOfXotsbg22BvL/dEWLr4WVdruli32LbArxXd73IVPTYi3TTjYV+zRrPzK +9WoBK/iFClfKcdT4NTY82llQesuUNu640lEJtT2G3Iba8UZnohyzm/S+UbeU65z8DKD5co +P7+QehxQSV+kj2BZnTi0WEwsD+GTznJYR5rvUsJCCAzoISsWrncSSgOQhF2XeW/T4ewvH+ +njLZViEhdG8R3kkdDjJG91OrSgrEqlk6Qhz1xEsv1rCOR69En7EJP3TNNrymPXPASrAnuE +HQkrVgGUfGqyD1sw1e6nBfNWisuw+g99CieIB8EI9WwpxQdKqWNU9Hjx+SAdC3NrPjAAAA +wQCo0hUGjSf6xhcgeaNa0gWSKEVuFhxR/FaCPTKadV7Md0APW4toeQZDujzDFlCZbQTZjC +0723B4lKugDzXfsOgvOTKp4vEjZOu0YGruS00LFWM7Sutdzx68b/ZMFALzITt/myKVMdpv +WpaO+3+PyEYIQH44QrSWw7cKLzNiZ8kt2drPkPktub4o2h5TdIBluEQLJDPMejy8IqQEU8 +aOyJOMvYxAbGAWY7Ck9DGlcJgaFdORROW8d8ZGrHQkyRl41JQAAADBAPeUMsrbI17wPP/s +Tsrkms5ws5yTz0xle2Wn6HwDSzQRSdn5abnIDYb3QSy0nRBvczef6ssH65dl50+2V4BV2L +MwHcmKD6/UoFsWwP/RMf1EoacPFiEAWJGxFbOthNX+rx5BpbUHNoQd8xby+88saDI0e8W6 +36HPBZrAZhQljkMa4OJqZDDCpOJvSndXwkZ789E96uprKopJZGwlLmfMtikQpNXT9R+I0b +SQCJj4yNakcdOE/7UifkOR02u+pgux3wAAAMEA5wdelKwGQ0EdkIF2TM844uLPszo3ZSH+ +Heff/Lbxs1Y+oL0NTJQicwMF0d9WEwBoTZJpuzsQEA1zkfmW0gi2womIRmiY0ZhpxbBuhO +6XePMIhUfQmWWjaUbAkrNB0eJkSTuUGzwxVkMXehrMuj4gYe8GMC8GgULbP0A8FjH01fKk +jFwgg4WAg6zUTpck12bh49NZRFyXIbXNk/jjxJtb0p//5TRTUQ6mR5IloaNTM23EiF6tle +Y6CAchnyhHO0BTAAAACWhhemVAaGF6ZQE= +-----END OPENSSH PRIVATE KEY----- diff --git a/certificates/sftp/id_rsa.pub b/certificates/sftp/id_rsa.pub new file mode 100644 index 0000000..81a5db6 --- /dev/null +++ b/certificates/sftp/id_rsa.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDfbdpaofpjBEtsIE73hQo6R+cnabpmyJzR0y3dOYOeMtTQnOk63vC3n6YxmsQpBOl5+jPJQejfP4eSaWUsAY66k3LcD6MbY73nlRSDDR0Z+7qPo45vroO72Omq3wFDqCvdwRgBKmU8jsHzz39xWknmXUeIFSDJHtnXq/XmoIO2akVUgQIB0gISY+ix+Zm623dhiX3EtrLlYKZTO1ZL9qY9wazQM4QatZ4K13EYpZPdPLB0ErJDQ9t1gxqaAMhPslXVlLNk9qXLiwHsT52lLkCAEVVeZnrXXnUo3VMnyDKhSMWFjty1gQimdxmbJPg9i/Ii/FGxsm28RGJ40fwOhfkPC2pzFuAcrQ7Bi12XjJ1FdOhijeX6oiJ1/6DwANV5r+ZuR9M96B1Oijcs65OFc7ATEOW6SkzyhsUOnPViXUvqtEkBU07UqYdaGfq2jbeO1iMGFzMDaeR6ZBdBtVKNz+Ei2NAMgU0w6wtbfc/5jaT0uSkjRMvT2iA7E4VnMZmna00= haze@haze diff --git a/src/main.rs b/src/main.rs index 2e4de9b..a58e922 100644 --- a/src/main.rs +++ b/src/main.rs @@ -633,12 +633,7 @@ async fn setup(docker: &Docker, options: CloudOptions, config: &HazeConfig) -> R for service in cloud.services() { for cmd in service.post_setup(docker, &cloud.id, config).await? { cloud - .exec( - docker, - shell_words::split(&cmd).into_diagnostic()?, - false, - Vec::::default(), - ) + .exec(docker, cmd, false, Vec::::default()) .await?; } } diff --git a/src/service.rs b/src/service.rs index 490f617..cb517cb 100644 --- a/src/service.rs +++ b/src/service.rs @@ -30,7 +30,7 @@ pub use crate::service::office::Office; pub use crate::service::onlyoffice::OnlyOffice; pub use crate::service::push::NotifyPush; use crate::service::redis::Redis; -use crate::service::sftp::Sftp; +use crate::service::sftp::{Sftp, SftpKey}; use crate::service::sharded::{Sharding, ShardingMigrate, ShardingMigrateUnset, SingleShard}; use crate::service::smb::Smb; use crate::service::webhook::Webhook; @@ -116,7 +116,7 @@ pub trait ServiceTrait { _docker: &Docker, _cloud_id: &str, _config: &HazeConfig, - ) -> Result> { + ) -> Result>> { Ok(Vec::new()) } @@ -267,6 +267,8 @@ pub enum ServiceType { Dav, /// Sftp external storage Sftp, + /// Sftp external storage with public key authentication + SftpKey, /// ownCloud instance for migration Oc, /// Imaginary for preview generation @@ -318,6 +320,7 @@ pub enum Service { ShardingMigrate(ShardingMigrate), ShardingMigrateUnset(ShardingMigrateUnset), Sftp(Sftp), + SftpKey(SftpKey), Kaspersky(Kaspersky), KasperskyIcap(KasperskyIcap), Clam(Clam), @@ -361,6 +364,7 @@ impl Service { } ServiceType::Dav => Some(vec![Service::Dav(Dav)]), ServiceType::Sftp => Some(vec![Service::Sftp(Sftp)]), + ServiceType::SftpKey => Some(vec![Service::SftpKey(SftpKey)]), ServiceType::Oc => Some(vec![Service::Oc(Oc)]), ServiceType::Imaginary => Some(vec![Service::Imaginary(Imaginary)]), ServiceType::Kaspersky => Some(vec![Service::Kaspersky(Kaspersky)]), @@ -437,15 +441,29 @@ impl ServiceTrait for PresetService { _docker: &Docker, _cloud_id: &str, config: &HazeConfig, - ) -> Result> { + ) -> Result>> { let preset = get_preset(&config.preset, &self.0).ok_or_else(|| Report::msg("invalid preset"))?; let mut commands: Vec<_> = preset .apps .iter() - .map(|app| format!("occ app:enable {app} --force")) + .map(|app| { + vec![ + "occ".into(), + "app:enable".into(), + app.clone(), + "--force".into(), + ] + }) .collect(); - commands.extend_from_slice(&preset.commands); + for cmnd in &preset.commands { + commands.push(shell_words::split(cmnd).into_diagnostic()?); + } + Ok(commands) } } + +fn split_cmnd(s: &str) -> Vec { + s.split(' ').map(String::from).collect() +} diff --git a/src/service/clam.rs b/src/service/clam.rs index 74b790d..7221309 100644 --- a/src/service/clam.rs +++ b/src/service/clam.rs @@ -2,7 +2,7 @@ use crate::cloud::CloudOptions; use crate::config::HazeConfig; use crate::exec::exec; use crate::image::pull_image; -use crate::service::ServiceTrait; +use crate::service::{split_cmnd, ServiceTrait}; use crate::Result; use bollard::models::{ContainerCreateBody, EndpointSettings, HostConfig, NetworkingConfig}; use bollard::query_parameters::CreateContainerOptions; @@ -85,14 +85,13 @@ impl ServiceTrait for ClamIcap { _docker: &Docker, _cloud_id: &str, _config: &HazeConfig, - ) -> Result> { + ) -> Result>> { Ok(vec![ - "occ config:app:set files_antivirus av_mode --value=icap".into(), - "occ config:app:set files_antivirus av_host --value=clamav-icap".into(), - "occ config:app:set files_antivirus av_port --value=1344".into(), - "occ config:app:set files_antivirus av_icap_request_service --value=avscan".into(), - "occ config:app:set files_antivirus av_icap_response_header --value=X-Infection-Found" - .into(), + split_cmnd("occ config:app:set files_antivirus av_mode --value=icap"), + split_cmnd("occ config:app:set files_antivirus av_host --value=clamav-icap"), + split_cmnd("occ config:app:set files_antivirus av_port --value=1344"), + split_cmnd("occ config:app:set files_antivirus av_icap_request_service --value=avscan"), + split_cmnd("occ config:app:set files_antivirus av_icap_response_header --value=X-Infection-Found"), ]) } } @@ -171,7 +170,7 @@ impl ServiceTrait for ClamIcapTls { docker: &Docker, cloud_id: &str, config: &HazeConfig, - ) -> Result> { + ) -> Result>> { let mut cert = Vec::new(); exec( docker, @@ -191,14 +190,13 @@ impl ServiceTrait for ClamIcapTls { .wrap_err("Failed to write icap certificate")?; Ok(vec![ - "occ config:app:set files_antivirus av_mode --value=icap".into(), - "occ config:app:set files_antivirus av_icap_tls --value=1".into(), - "occ config:app:set files_antivirus av_host --value=clamav-icap-tls".into(), - "occ config:app:set files_antivirus av_port --value=1345".into(), - "occ config:app:set files_antivirus av_icap_request_service --value=avscan".into(), - "occ config:app:set files_antivirus av_icap_response_header --value=X-Infection-Found" - .into(), - "occ security:certificates:import data/icap-cert.pem".into(), + split_cmnd("occ config:app:set files_antivirus av_mode --value=icap"), + split_cmnd("occ config:app:set files_antivirus av_icap_tls --value=1"), + split_cmnd("occ config:app:set files_antivirus av_host --value=clamav-icap-tls"), + split_cmnd("occ config:app:set files_antivirus av_port --value=1345"), + split_cmnd("occ config:app:set files_antivirus av_icap_request_service --value=avscan"), + split_cmnd("occ config:app:set files_antivirus av_icap_response_header --value=X-Infection-Found"), + split_cmnd("occ security:certificates:import data/icap-cert.pem"), ]) } } @@ -221,10 +219,10 @@ impl ServiceTrait for Clam { _docker: &Docker, _cloud_id: &str, _config: &HazeConfig, - ) -> Result> { + ) -> Result>> { Ok(vec![ - "occ config:app:set files_antivirus av_mode --value=executable".into(), - "occ config:app:set files_antivirus av_path --value=/bin/clamscan".into(), + split_cmnd("occ config:app:set files_antivirus av_mode --value=executable"), + split_cmnd("occ config:app:set files_antivirus av_path --value=/bin/clamscan"), ]) } } @@ -294,10 +292,12 @@ impl ServiceTrait for ClamSocket { _docker: &Docker, _cloud_id: &str, _config: &HazeConfig, - ) -> Result> { + ) -> Result>> { Ok(vec![ - "occ config:app:set files_antivirus av_mode --value=socket".into(), - "occ config:app:set files_antivirus av_socket --value=tcp://clamav-socket:3310".into(), + split_cmnd("occ config:app:set files_antivirus av_mode --value=socket"), + split_cmnd( + "occ config:app:set files_antivirus av_socket --value=tcp://clamav-socket:3310", + ), ]) } } diff --git a/src/service/dav.rs b/src/service/dav.rs index d328925..e0b20f3 100644 --- a/src/service/dav.rs +++ b/src/service/dav.rs @@ -1,7 +1,7 @@ use crate::cloud::CloudOptions; use crate::config::HazeConfig; use crate::image::pull_image; -use crate::service::ServiceTrait; +use crate::service::{split_cmnd, ServiceTrait}; use crate::Result; use bollard::config::ContainerCreateBody; use bollard::models::{EndpointSettings, HostConfig, NetworkingConfig}; @@ -76,12 +76,12 @@ impl ServiceTrait for Dav { _docker: &Docker, _cloud_id: &str, _config: &HazeConfig, - ) -> Result> { + ) -> Result>> { Ok(vec![ - "occ files_external:create dav dav password::password".into(), - "occ files_external:config 1 host dav".into(), - "occ files_external:config 1 user test".into(), - "occ files_external:config 1 password test".into(), + split_cmnd("occ files_external:create dav dav password::password"), + split_cmnd("occ files_external:config 1 host dav"), + split_cmnd("occ files_external:config 1 user test"), + split_cmnd("occ files_external:config 1 password test"), ]) } } diff --git a/src/service/imaginary.rs b/src/service/imaginary.rs index 693c173..4ec4d96 100644 --- a/src/service/imaginary.rs +++ b/src/service/imaginary.rs @@ -1,7 +1,7 @@ use crate::cloud::CloudOptions; use crate::config::HazeConfig; use crate::image::pull_image; -use crate::service::ServiceTrait; +use crate::service::{split_cmnd, ServiceTrait}; use crate::Result; use bollard::config::NetworkingConfig; use bollard::models::{ContainerCreateBody, EndpointSettings, HostConfig}; @@ -71,11 +71,14 @@ impl ServiceTrait for Imaginary { _docker: &Docker, _cloud_id: &str, _config: &HazeConfig, - ) -> Result> { + ) -> Result>> { Ok(vec![ - "occ config:system:set enabledPreviewProviders 0 --value='OC\\Preview\\Imaginary'" - .into(), - "occ config:system:set preview_imaginary_url --value='http://imaginary:9000'".into(), + split_cmnd( + "occ config:system:set enabledPreviewProviders 0 --value='OC\\Preview\\Imaginary'", + ), + split_cmnd( + "occ config:system:set preview_imaginary_url --value='http://imaginary:9000'", + ), ]) } } diff --git a/src/service/kaspersky.rs b/src/service/kaspersky.rs index bc4f08a..1c0a67f 100644 --- a/src/service/kaspersky.rs +++ b/src/service/kaspersky.rs @@ -2,7 +2,7 @@ use crate::cloud::CloudOptions; use crate::config::HazeConfig; use crate::exec::exec; use crate::image::{image_exists, pull_image}; -use crate::service::ServiceTrait; +use crate::service::{split_cmnd, ServiceTrait}; use crate::Result; use bollard::models::{ContainerCreateBody, EndpointSettings, HostConfig, NetworkingConfig}; use bollard::query_parameters::CreateContainerOptions; @@ -101,11 +101,11 @@ impl ServiceTrait for Kaspersky { _docker: &Docker, _cloud_id: &str, _config: &HazeConfig, - ) -> Result> { + ) -> Result>> { Ok(vec![ - "occ config:app:set files_antivirus av_mode --value=kaspersky".into(), - "occ config:app:set files_antivirus av_host --value=kaspersky".into(), - "occ config:app:set files_antivirus av_port --value=80".into(), + split_cmnd("occ config:app:set files_antivirus av_mode --value=kaspersky"), + split_cmnd("occ config:app:set files_antivirus av_host --value=kaspersky"), + split_cmnd("occ config:app:set files_antivirus av_port --value=80"), ]) } } @@ -187,13 +187,15 @@ impl ServiceTrait for KasperskyIcap { _docker: &Docker, _cloud_id: &str, _config: &HazeConfig, - ) -> Result> { + ) -> Result>> { Ok(vec![ - "occ config:app:set files_antivirus av_mode --value=icap".into(), - "occ config:app:set files_antivirus av_host --value=kaspersky-icap".into(), - "occ config:app:set files_antivirus av_port --value=1344".into(), - "occ config:app:set files_antivirus av_icap_request_service --value=req".into(), - "occ config:app:set files_antivirus av_icap_response_header --value=X-Virus-ID".into(), + split_cmnd("occ config:app:set files_antivirus av_mode --value=icap"), + split_cmnd("occ config:app:set files_antivirus av_host --value=kaspersky-icap"), + split_cmnd("occ config:app:set files_antivirus av_port --value=1344"), + split_cmnd("occ config:app:set files_antivirus av_icap_request_service --value=req"), + split_cmnd( + "occ config:app:set files_antivirus av_icap_response_header --value=X-Virus-ID", + ), ]) } } diff --git a/src/service/ldap.rs b/src/service/ldap.rs index bd41ba3..0f22643 100644 --- a/src/service/ldap.rs +++ b/src/service/ldap.rs @@ -1,7 +1,7 @@ use crate::cloud::CloudOptions; use crate::config::{HazeConfig, ProxyConfig}; use crate::image::pull_image; -use crate::service::ServiceTrait; +use crate::service::{split_cmnd, ServiceTrait}; use crate::Result; use bollard::config::NetworkingConfig; use bollard::models::{ContainerCreateBody, ContainerState, EndpointSettings, HostConfig}; @@ -92,30 +92,29 @@ impl ServiceTrait for Ldap { _docker: &Docker, _cloud_id: &str, _config: &HazeConfig, - ) -> Result> { + ) -> Result>> { Ok(vec![ - "occ ldap:create-empty-config".into(), - "occ ldap:set-config s01 ldapHost 'ldap://ldap'".into(), - "occ ldap:set-config s01 ldapPort '389'".into(), - "occ ldap:set-config s01 ldapAgentName 'cn=admin,dc=example,dc=org'".into(), - "occ ldap:set-config s01 ldapAgentPassword 'haze'".into(), - "occ ldap:set-config s01 ldapBase 'dc=example,dc=org'".into(), - "occ ldap:set-config s01 ldapBaseUsers 'dc=example,dc=org'".into(), - "occ ldap:set-config s01 ldapBaseGroups 'dc=example,dc=org'".into(), - "occ ldap:set-config s01 ldapLoginFilter '(&(&(objectclass=inetOrgPerson))(uid=%uid))'" - .into(), - "occ ldap:set-config s01 ldapUserFilter '((objectclass=inetOrgPerson))'".into(), - "occ ldap:set-config s01 ldapUserFilterMode '0'".into(), - "occ ldap:set-config s01 ldapUserDisplayName 'sn'".into(), - "occ ldap:set-config s01 ldapUserFilterObjectclass 'inetOrgPerson'".into(), - "occ ldap:set-config s01 ldapGroupFilter '(&(|(objectclass=posixGroup)))'".into(), - "occ ldap:set-config s01 ldapGroupFilterObjectclass 'posixGroup'".into(), - "occ ldap:set-config s01 ldapEmailAttribute 'email'".into(), - "occ ldap:set-config s01 ldapUuidUserAttribute 'email'".into(), - "occ ldap:set-config s01 ldapUuidUserAttribute 'auto'".into(), - "occ ldap:set-config s01 ldapUuidGroupAttribute 'auto'".into(), - "occ ldap:set-config s01 ldapLoginFilterUsername '1'".into(), - "occ ldap:set-config s01 ldapConfigurationActive '1'".into(), + split_cmnd("occ ldap:create-empty-config"), + split_cmnd("occ ldap:set-config s01 ldapHost 'ldap://ldap'"), + split_cmnd("occ ldap:set-config s01 ldapPort '389'"), + split_cmnd("occ ldap:set-config s01 ldapAgentName 'cn=admin,dc=example,dc=org'"), + split_cmnd("occ ldap:set-config s01 ldapAgentPassword 'haze'"), + split_cmnd("occ ldap:set-config s01 ldapBase 'dc=example,dc=org'"), + split_cmnd("occ ldap:set-config s01 ldapBaseUsers 'dc=example,dc=org'"), + split_cmnd("occ ldap:set-config s01 ldapBaseGroups 'dc=example,dc=org'"), + split_cmnd("occ ldap:set-config s01 ldapLoginFilter '(&(&(objectclass=inetOrgPerson))(uid=%uid))'"), + split_cmnd("occ ldap:set-config s01 ldapUserFilter '((objectclass=inetOrgPerson))'"), + split_cmnd("occ ldap:set-config s01 ldapUserFilterMode '0'"), + split_cmnd("occ ldap:set-config s01 ldapUserDisplayName 'sn'"), + split_cmnd("occ ldap:set-config s01 ldapUserFilterObjectclass 'inetOrgPerson'"), + split_cmnd("occ ldap:set-config s01 ldapGroupFilter '(&(|(objectclass=posixGroup)))'"), + split_cmnd("occ ldap:set-config s01 ldapGroupFilterObjectclass 'posixGroup'"), + split_cmnd("occ ldap:set-config s01 ldapEmailAttribute 'email'"), + split_cmnd("occ ldap:set-config s01 ldapUuidUserAttribute 'email'"), + split_cmnd("occ ldap:set-config s01 ldapUuidUserAttribute 'auto'"), + split_cmnd("occ ldap:set-config s01 ldapUuidGroupAttribute 'auto'"), + split_cmnd("occ ldap:set-config s01 ldapLoginFilterUsername '1'"), + split_cmnd("occ ldap:set-config s01 ldapConfigurationActive '1'"), ]) } diff --git a/src/service/mail.rs b/src/service/mail.rs index c52fdac..ad40471 100644 --- a/src/service/mail.rs +++ b/src/service/mail.rs @@ -1,7 +1,7 @@ use crate::cloud::CloudOptions; use crate::config::HazeConfig; use crate::image::pull_image; -use crate::service::ServiceTrait; +use crate::service::{split_cmnd, ServiceTrait}; use crate::Result; use bollard::models::{ContainerCreateBody, EndpointSettings, HostConfig, NetworkingConfig}; use bollard::query_parameters::CreateContainerOptions; @@ -70,14 +70,14 @@ impl ServiceTrait for Mail { _docker: &Docker, _cloud_id: &str, _config: &HazeConfig, - ) -> Result> { + ) -> Result>> { Ok(vec![ - "occ config:system:set mail_smtpmode --value smtp".into(), - "occ config:system:set mail_sendmailmode --value smtp".into(), - "occ config:system:set mail_domain --value haze".into(), - "occ config:system:set mail_smtphost --value mail".into(), - "occ config:system:set mail_smtpport --value 25".into(), - "occ user:setting admin settings email admin@haze".into(), + split_cmnd("occ config:system:set mail_smtpmode --value smtp"), + split_cmnd("occ config:system:set mail_sendmailmode --value smtp"), + split_cmnd("occ config:system:set mail_domain --value haze"), + split_cmnd("occ config:system:set mail_smtphost --value mail"), + split_cmnd("occ config:system:set mail_smtpport --value 25"), + split_cmnd("occ user:setting admin settings email admin@haze"), ]) } } diff --git a/src/service/objectstore.rs b/src/service/objectstore.rs index c0767e0..20e586c 100644 --- a/src/service/objectstore.rs +++ b/src/service/objectstore.rs @@ -2,7 +2,7 @@ use crate::cloud::CloudOptions; use crate::config::HazeConfig; use crate::exec::exec; use crate::image::pull_image; -use crate::service::ServiceTrait; +use crate::service::{split_cmnd, ServiceTrait}; use crate::Result; use bollard::models::{ ContainerCreateBody, ContainerState, EndpointSettings, HostConfig, NetworkingConfig, @@ -247,18 +247,18 @@ impl ServiceTrait for ObjectStore { _docker: &Docker, _cloud_id: &str, _config: &HazeConfig, - ) -> Result> { + ) -> Result>> { match self { ObjectStore::S3 => Ok(vec![ - "occ files_external:create s3 amazons3 amazons3::accesskey".into(), - "occ files_external:config 1 bucket ext".into(), - "occ files_external:config 1 hostname s3".into(), - "occ files_external:config 1 port 9000".into(), - "occ files_external:config 1 use_ssl false".into(), - "occ files_external:config 1 use_path_style true".into(), - "occ files_external:config 1 key minio".into(), - "occ files_external:config 1 secret minio123".into(), - "mc alias set s3 http://s3:9000 minio minio123".into(), + split_cmnd("occ files_external:create s3 amazons3 amazons3::accesskey"), + split_cmnd("occ files_external:config 1 bucket ext"), + split_cmnd("occ files_external:config 1 hostname s3"), + split_cmnd("occ files_external:config 1 port 9000"), + split_cmnd("occ files_external:config 1 use_ssl false"), + split_cmnd("occ files_external:config 1 use_path_style true"), + split_cmnd("occ files_external:config 1 key minio"), + split_cmnd("occ files_external:config 1 secret minio123"), + split_cmnd("mc alias set s3 http://s3:9000 minio minio123"), ]), // ObjectStore::S3s => Ok(vec![ // "occ files_external:create s3 amazons3 amazons3::accesskey".into(), diff --git a/src/service/oc.rs b/src/service/oc.rs index 2eb3a22..296b2a5 100644 --- a/src/service/oc.rs +++ b/src/service/oc.rs @@ -83,7 +83,7 @@ impl ServiceTrait for Oc { docker: &Docker, cloud_id: &str, config: &HazeConfig, - ) -> Result> { + ) -> Result>> { if let Some(ip) = self.get_ips(docker, cloud_id).await?.next() { let container = self.container_name(cloud_id).unwrap(); let addr = config.proxy.addr(&container, ip); diff --git a/src/service/office.rs b/src/service/office.rs index 8aaf99a..e7db10d 100644 --- a/src/service/office.rs +++ b/src/service/office.rs @@ -119,7 +119,7 @@ impl ServiceTrait for Office { docker: &Docker, cloud_id: &str, config: &HazeConfig, - ) -> Result> { + ) -> Result>> { let container = &self.container_name(cloud_id).unwrap(); let info = docker .inspect_container(container, None) @@ -152,8 +152,22 @@ impl ServiceTrait for Office { .addr_with_port(container, ip, self.proxy_port()); Ok(vec![ - format!(r#"occ config:app:set richdocuments public_wopi_url --value="{public}""#), - r#"occ richdocuments:setup --wopi-url "http://office:9980" --callback-url "http://cloud""#.into(), + vec![ + "occ".into(), + "config:app:set".into(), + "richdocuments".into(), + "public_wopi_url".into(), + "--value".into(), + public, + ], + vec![ + "occ".into(), + "richdocuments:setup".into(), + "--wopi-url".into(), + "http://office:9980".into(), + "--callback-url".into(), + "http://cloud".into(), + ], ]) } diff --git a/src/service/onlyoffice.rs b/src/service/onlyoffice.rs index b7c8a14..fa12c2c 100644 --- a/src/service/onlyoffice.rs +++ b/src/service/onlyoffice.rs @@ -2,7 +2,7 @@ use crate::cloud::CloudOptions; use crate::config::HazeConfig; use crate::exec::exec; use crate::image::pull_image; -use crate::service::ServiceTrait; +use crate::service::{split_cmnd, ServiceTrait}; use crate::Result; use bollard::models::{ ContainerCreateBody, ContainerState, EndpointSettings, HostConfig, NetworkingConfig, @@ -82,7 +82,7 @@ impl ServiceTrait for OnlyOffice { docker: &Docker, cloud_id: &str, config: &HazeConfig, - ) -> Result> { + ) -> Result>> { let info = docker .inspect_container(&self.container_name(cloud_id).unwrap(), None) .await @@ -137,16 +137,44 @@ impl ServiceTrait for OnlyOffice { ); Ok(vec![ - format!("occ config:app:set onlyoffice DocumentServerUrl --value {addr}/"), - format!("occ config:app:set onlyoffice jwt_secret --value {secret}"), - "occ onlyoffice:documentserver --check".into(), + vec![ + "occ".into(), + "config:app:set".into(), + "onlyoffice".into(), + "DocumentServerUrl".into(), + "--value".into(), + addr, + ], + vec![ + "occ".into(), + "config:app:set".into(), + "onlyoffice".into(), + "jwt_secret".into(), + "--value".into(), + secret.into(), + ], + split_cmnd("occ onlyoffice:documentserver --check"), ]) } else { Ok(vec![ - format!("occ config:app:set onlyoffice DocumentServerUrl --value https://{ip}/"), - "occ config:app:set onlyoffice verify_peer_off --value true".into(), - format!("occ config:app:set onlyoffice jwt_secret --value {secret}"), - "occ onlyoffice:documentserver --check".into(), + vec![ + "occ".into(), + "config:app:set".into(), + "onlyoffice".into(), + "DocumentServerUrl".into(), + "--value".into(), + format!("https://{ip}/"), + ], + split_cmnd("occ config:app:set onlyoffice verify_peer_off --value true"), + vec![ + "occ".into(), + "config:app:set".into(), + "onlyoffice".into(), + "jwt_secret".into(), + "--value".into(), + secret.into(), + ], + split_cmnd("occ onlyoffice:documentserver --check"), ]) } } diff --git a/src/service/push.rs b/src/service/push.rs index 22b3ac2..b0bbab2 100644 --- a/src/service/push.rs +++ b/src/service/push.rs @@ -87,7 +87,7 @@ impl ServiceTrait for NotifyPush { docker: &Docker, cloud_id: &str, config: &HazeConfig, - ) -> Result> { + ) -> Result>> { let mut ips: Vec<_> = self.get_ips(docker, cloud_id).await?.collect(); if let Ok(local_interfaces) = list_afinet_netifas() { ips.extend(local_interfaces.into_iter().map(|(_, ip)| ip)); @@ -97,10 +97,14 @@ impl ServiceTrait for NotifyPush { .iter() .enumerate() .map(|(i, ip)| { - format!( - "occ config:system:set trusted_proxies {} --value {ip}", - i + 1 - ) + vec![ + "occ".into(), + "config:system:set".into(), + "trusted_proxies".into(), + (i + 1).to_string(), + "--value".into(), + ip.to_string(), + ] }) .collect(); @@ -108,7 +112,7 @@ impl ServiceTrait for NotifyPush { config .proxy .addr_with_port(&self.container_name(cloud_id).unwrap(), ips[0], 7867); - commands.push(format!("occ notify_push:setup {}", addr)); + commands.push(vec!["occ".into(), "notify_push:setup".into(), addr]); Ok(commands) } diff --git a/src/service/redis.rs b/src/service/redis.rs index 29a22ee..db69667 100644 --- a/src/service/redis.rs +++ b/src/service/redis.rs @@ -1,7 +1,7 @@ use crate::cloud::CloudOptions; use crate::config::HazeConfig; use crate::image::pull_image; -use crate::service::ServiceTrait; +use crate::service::{split_cmnd, ServiceTrait}; use crate::Result; use bollard::models::{ContainerCreateBody, EndpointSettings, HostConfig, NetworkingConfig}; use bollard::query_parameters::CreateContainerOptions; @@ -70,7 +70,9 @@ impl ServiceTrait for Redis { _docker: &Docker, _cloud_id: &str, _config: &HazeConfig, - ) -> Result> { - Ok(vec!["occ config:system:set redis host --value redis".into()]) + ) -> Result>> { + Ok(vec![split_cmnd( + "occ config:system:set redis host --value redis", + )]) } } diff --git a/src/service/sftp.rs b/src/service/sftp.rs index 80405e2..c414b16 100644 --- a/src/service/sftp.rs +++ b/src/service/sftp.rs @@ -1,13 +1,14 @@ use crate::cloud::CloudOptions; use crate::config::HazeConfig; use crate::image::pull_image; -use crate::service::ServiceTrait; +use crate::service::{split_cmnd, ServiceTrait}; use crate::Result; use bollard::models::{ContainerCreateBody, EndpointSettings, HostConfig, NetworkingConfig}; use bollard::query_parameters::CreateContainerOptions; use bollard::Docker; use maplit::hashmap; -use miette::IntoDiagnostic; +use miette::{Context, IntoDiagnostic}; +use std::fs::{create_dir_all, write}; #[derive(Debug, Clone, Eq, PartialEq)] pub struct Sftp; @@ -75,13 +76,119 @@ impl ServiceTrait for Sftp { _docker: &Docker, _cloud_id: &str, _config: &HazeConfig, - ) -> Result> { + ) -> Result>> { Ok(vec![ - "occ files_external:create sftp sftp password::password".into(), - "occ files_external:config 1 host sftp".into(), - "occ files_external:config 1 user test".into(), - "occ files_external:config 1 root data".into(), - "occ files_external:config 1 password test".into(), + split_cmnd("occ files_external:create sftp sftp password::password"), + split_cmnd("occ files_external:config 1 host sftp"), + split_cmnd("occ files_external:config 1 user test"), + split_cmnd("occ files_external:config 1 root data"), + split_cmnd("occ files_external:config 1 password test"), + ]) + } +} + +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct SftpKey; + +#[async_trait::async_trait] +impl ServiceTrait for SftpKey { + fn name(&self) -> &str { + "sftp-key" + } + + async fn spawn( + &self, + docker: &Docker, + cloud_id: &str, + network: &str, + config: &HazeConfig, + _options: &CloudOptions, + ) -> Result> { + let image = "atmoz/sftp:alpine"; + pull_image(docker, image).await?; + let options = Some(CreateContainerOptions { + name: self.container_name(cloud_id), + ..CreateContainerOptions::default() + }); + let key_dir = config.work_dir.join("certificates/sftp"); + create_dir_all(&key_dir) + .into_diagnostic() + .wrap_err("Failed to create sftp certificate directory")?; + let private_path = key_dir.join("id_rsa"); + let public_path = key_dir.join("id_rsa.pub"); + let private_key = include_str!("../../certificates/sftp/id_rsa"); + let public_key = include_str!("../../certificates/sftp/id_rsa.pub"); + if !private_path.exists() { + write(&private_path, private_key) + .into_diagnostic() + .wrap_err("Failed to write sftp client certificate")?; + } + if !public_path.exists() { + write(&public_path, public_key) + .into_diagnostic() + .wrap_err("Failed to write sftp client key")?; + } + + let volumes = vec![format!("{public_path}:/home/test/.ssh/keys/id_rsa:ro")]; + + let config = ContainerCreateBody { + image: Some(image.into()), + host_config: Some(HostConfig { + network_mode: Some(network.to_string()), + binds: Some(volumes), + ..Default::default() + }), + labels: Some(hashmap! { + "haze-type".into() => self.name().into(), + "haze-cloud-id".into() => cloud_id.into(), + }), + networking_config: Some(NetworkingConfig { + endpoints_config: Some(hashmap! { + network.into() => EndpointSettings { + aliases: Some(vec![self.name().to_string()]), + ..Default::default() + } + }), + }), + cmd: Some(vec!["test::::data".into()]), + ..Default::default() + }; + let id = docker + .create_container(options, config) + .await + .into_diagnostic()? + .id; + docker.start_container(&id, None).await.into_diagnostic()?; + Ok(vec![id]) + } + + fn container_name(&self, cloud_id: &str) -> Option { + Some(format!("{}-sftp-key", cloud_id)) + } + + fn apps(&self) -> &'static [&'static str] { + &["files_external"] + } + + async fn post_setup( + &self, + _docker: &Docker, + _cloud_id: &str, + _config: &HazeConfig, + ) -> Result>> { + Ok(vec![ + split_cmnd("occ files_external:create sftp sftp publickey::rsa_private"), + split_cmnd("occ files_external:config 1 host sftp-key"), + split_cmnd("occ files_external:config 1 user test"), + split_cmnd("occ files_external:config 1 root data"), + vec![ + "occ".into(), + "files_external:config".into(), + "--value-from-file".into(), + "1".into(), + "private_key".into(), + "/certificates/sftp/id_rsa".into(), + ], ]) } } diff --git a/src/service/smb.rs b/src/service/smb.rs index a40b105..da8f1f6 100644 --- a/src/service/smb.rs +++ b/src/service/smb.rs @@ -1,7 +1,7 @@ use crate::cloud::CloudOptions; use crate::config::HazeConfig; use crate::image::pull_image; -use crate::service::ServiceTrait; +use crate::service::{split_cmnd, ServiceTrait}; use crate::Result; use bollard::models::{ContainerCreateBody, EndpointSettings, HostConfig, NetworkingConfig}; use bollard::query_parameters::CreateContainerOptions; @@ -79,13 +79,13 @@ impl ServiceTrait for Smb { _docker: &Docker, _cloud_id: &str, _config: &HazeConfig, - ) -> Result> { + ) -> Result>> { Ok(vec![ - "occ files_external:create smb smb password::password".into(), - "occ files_external:config 1 host smb".into(), - "occ files_external:config 1 user test".into(), - "occ files_external:config 1 password test".into(), - "occ files_external:config 1 share test".into(), + split_cmnd("occ files_external:create smb smb password::password"), + split_cmnd("occ files_external:config 1 host smb"), + split_cmnd("occ files_external:config 1 user test"), + split_cmnd("occ files_external:config 1 password test"), + split_cmnd("occ files_external:config 1 share test"), ]) } } From 92fbc74a5b7bb5d4f309b22cdbe690705ffadb6a Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Thu, 28 May 2026 18:40:20 +0200 Subject: [PATCH 66/68] fix ldap --- src/service/ldap.rs | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/service/ldap.rs b/src/service/ldap.rs index 0f22643..4f317e6 100644 --- a/src/service/ldap.rs +++ b/src/service/ldap.rs @@ -95,26 +95,26 @@ impl ServiceTrait for Ldap { ) -> Result>> { Ok(vec![ split_cmnd("occ ldap:create-empty-config"), - split_cmnd("occ ldap:set-config s01 ldapHost 'ldap://ldap'"), - split_cmnd("occ ldap:set-config s01 ldapPort '389'"), - split_cmnd("occ ldap:set-config s01 ldapAgentName 'cn=admin,dc=example,dc=org'"), - split_cmnd("occ ldap:set-config s01 ldapAgentPassword 'haze'"), - split_cmnd("occ ldap:set-config s01 ldapBase 'dc=example,dc=org'"), - split_cmnd("occ ldap:set-config s01 ldapBaseUsers 'dc=example,dc=org'"), - split_cmnd("occ ldap:set-config s01 ldapBaseGroups 'dc=example,dc=org'"), - split_cmnd("occ ldap:set-config s01 ldapLoginFilter '(&(&(objectclass=inetOrgPerson))(uid=%uid))'"), - split_cmnd("occ ldap:set-config s01 ldapUserFilter '((objectclass=inetOrgPerson))'"), - split_cmnd("occ ldap:set-config s01 ldapUserFilterMode '0'"), - split_cmnd("occ ldap:set-config s01 ldapUserDisplayName 'sn'"), - split_cmnd("occ ldap:set-config s01 ldapUserFilterObjectclass 'inetOrgPerson'"), - split_cmnd("occ ldap:set-config s01 ldapGroupFilter '(&(|(objectclass=posixGroup)))'"), - split_cmnd("occ ldap:set-config s01 ldapGroupFilterObjectclass 'posixGroup'"), - split_cmnd("occ ldap:set-config s01 ldapEmailAttribute 'email'"), - split_cmnd("occ ldap:set-config s01 ldapUuidUserAttribute 'email'"), - split_cmnd("occ ldap:set-config s01 ldapUuidUserAttribute 'auto'"), - split_cmnd("occ ldap:set-config s01 ldapUuidGroupAttribute 'auto'"), - split_cmnd("occ ldap:set-config s01 ldapLoginFilterUsername '1'"), - split_cmnd("occ ldap:set-config s01 ldapConfigurationActive '1'"), + split_cmnd("occ ldap:set-config s01 ldapHost ldap://ldap"), + split_cmnd("occ ldap:set-config s01 ldapPort 389"), + split_cmnd("occ ldap:set-config s01 ldapAgentName cn=admin,dc=example,dc=org"), + split_cmnd("occ ldap:set-config s01 ldapAgentPassword haze"), + split_cmnd("occ ldap:set-config s01 ldapBase dc=example,dc=org"), + split_cmnd("occ ldap:set-config s01 ldapBaseUsers dc=example,dc=org"), + split_cmnd("occ ldap:set-config s01 ldapBaseGroups dc=example,dc=org"), + split_cmnd("occ ldap:set-config s01 ldapLoginFilter (&(&(objectclass=inetOrgPerson))(uid=%uid))"), + split_cmnd("occ ldap:set-config s01 ldapUserFilter ((objectclass=inetOrgPerson))"), + split_cmnd("occ ldap:set-config s01 ldapUserFilterMode 0"), + split_cmnd("occ ldap:set-config s01 ldapUserDisplayName sn"), + split_cmnd("occ ldap:set-config s01 ldapUserFilterObjectclass inetOrgPerson"), + split_cmnd("occ ldap:set-config s01 ldapGroupFilter (&(|(objectclass=posixGroup)))"), + split_cmnd("occ ldap:set-config s01 ldapGroupFilterObjectclass posixGroup"), + split_cmnd("occ ldap:set-config s01 ldapEmailAttribute email"), + split_cmnd("occ ldap:set-config s01 ldapUuidUserAttribute email"), + split_cmnd("occ ldap:set-config s01 ldapUuidUserAttribute auto"), + split_cmnd("occ ldap:set-config s01 ldapUuidGroupAttribute auto"), + split_cmnd("occ ldap:set-config s01 ldapLoginFilterUsername 1"), + split_cmnd("occ ldap:set-config s01 ldapConfigurationActive 1"), ]) } From d852f9db4fe4d7d7ddbe94a93b07f2771cd69e8a Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Thu, 28 May 2026 18:40:20 +0200 Subject: [PATCH 67/68] remove php-smbclient for now as it's broken --- nix/image/php-ext.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nix/image/php-ext.nix b/nix/image/php-ext.nix index 69d23e1..0a7e53e 100644 --- a/nix/image/php-ext.nix +++ b/nix/image/php-ext.nix @@ -38,7 +38,7 @@ in imagick ] ++ optionals (!debug) [ - smbclient # this breaks the build for no apparent reason + # smbclient # this breaks the build for no apparent reason ] ++ optionals withBlackfire [ blackfire From a3f2355deae7ebd87004fcb4a82588e1b35d7850 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Thu, 28 May 2026 18:40:20 +0200 Subject: [PATCH 68/68] create smb/sftp accounts for ldaptest --- src/service/sftp.rs | 5 ++++- src/service/smb.rs | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/service/sftp.rs b/src/service/sftp.rs index c414b16..1f47a40 100644 --- a/src/service/sftp.rs +++ b/src/service/sftp.rs @@ -51,7 +51,10 @@ impl ServiceTrait for Sftp { } }), }), - cmd: Some(vec!["test:test:::data".into()]), + cmd: Some(vec![ + "test:test:::data".into(), + "ldaptest:test:::data".into(), + ]), ..Default::default() }; let id = docker diff --git a/src/service/smb.rs b/src/service/smb.rs index da8f1f6..be1143b 100644 --- a/src/service/smb.rs +++ b/src/service/smb.rs @@ -40,8 +40,10 @@ impl ServiceTrait for Smb { }), env: Some(vec![ "ACCOUNT_test=test".into(), + "ACCOUNT_ldaptest=test".into(), "UID_test=1000".into(), "SAMBA_VOLUME_CONFIG_test=[test]; path=/tmp; valid users = test; guest ok = no; read only = no; browseable = yes".into(), + "SAMBA_VOLUME_CONFIG_ldaptest=[ldaptest]; path=/tmp; valid users = ldaptest; guest ok = no; read only = no; browseable = yes".into(), ]), labels: Some(hashmap! { "haze-type".into() => self.name().into(),