From 479797361e7fbda3f69927368a0d35a76e314828 Mon Sep 17 00:00:00 2001 From: Matt Hill Date: Tue, 21 Jan 2025 20:46:36 -0700 Subject: [PATCH] add clearnet functionality to frontend (#2814) * add clearnet functionality to frontend * add pattern and add sync db on rpcs * add domain pattern * show acme name instead of url if known * dont blow up if domain not present after delete * use common name for letsencrypt * normalize urls * refactor start-os ui net service * backend migration and rpcs for serverInfo.host * fix cors * implement clearnet for main startos ui * ability to add and remove tor addresses, including vanity * add guard to prevent duplicate addresses * misc bugfixes * better heuristics for launching UIs * fix ipv6 mocks * fix ipv6 display bug * rewrite url selection for launch ui --------- Co-authored-by: Aiden McClelland --- .../Systems/SystemForEmbassy/MainLoop.ts | 1 - .../Systems/SystemForEmbassy/index.ts | 1 + core/Cargo.lock | 327 ++++++------- core/helpers/Cargo.toml | 2 +- core/models/Cargo.toml | 2 +- core/models/src/data_url.rs | 2 +- core/models/src/errors.rs | 6 +- core/models/src/id/invalid_id.rs | 6 +- core/models/src/id/mod.rs | 6 +- core/startos/Cargo.toml | 6 +- core/startos/src/account.rs | 34 +- core/startos/src/backup/os.rs | 10 +- core/startos/src/backup/restore.rs | 6 +- core/startos/src/bins/start_init.rs | 6 +- core/startos/src/context/config.rs | 3 - core/startos/src/context/rpc.rs | 57 ++- core/startos/src/context/setup.rs | 8 +- core/startos/src/db/model/public.rs | 54 ++- core/startos/src/init.rs | 80 ++-- core/startos/src/lib.rs | 5 +- core/startos/src/middleware/cors.rs | 10 +- core/startos/src/net/acme.rs | 69 ++- core/startos/src/net/dns.rs | 57 ++- core/startos/src/net/host/address.rs | 158 +++---- core/startos/src/net/host/binding.rs | 59 +-- core/startos/src/net/host/mod.rs | 136 ++++-- core/startos/src/net/keys.rs | 4 +- core/startos/src/net/net_controller.rs | 428 ++++++++++-------- core/startos/src/net/network_interface.rs | 58 ++- core/startos/src/net/vhost.rs | 29 +- core/startos/src/net/wifi.rs | 3 +- core/startos/src/service/effects/net/bind.rs | 19 +- core/startos/src/service/effects/net/info.rs | 3 +- core/startos/src/service/mod.rs | 21 +- .../src/service/persistent_container.rs | 4 +- core/startos/src/service/service_actor.rs | 32 -- core/startos/src/service/service_map.rs | 11 +- core/startos/src/setup.rs | 15 +- core/startos/src/util/sync.rs | 8 + core/startos/src/version/mod.rs | 6 +- core/startos/src/version/v0_3_6_alpha_0.rs | 4 +- core/startos/src/version/v0_3_6_alpha_12.rs | 68 +++ package-lock.json | 6 - patch-db | 2 +- sdk/base/lib/interfaces/Host.ts | 31 +- sdk/base/lib/interfaces/Origin.ts | 6 +- sdk/base/lib/osBindings/BindParams.ts | 2 - sdk/base/lib/osBindings/Host.ts | 2 - sdk/base/lib/osBindings/HostKind.ts | 3 - sdk/base/lib/osBindings/ServerInfo.ts | 8 +- sdk/base/lib/osBindings/SetupResult.ts | 2 +- sdk/base/lib/osBindings/index.ts | 31 +- sdk/base/lib/util/patterns.ts | 29 +- sdk/base/lib/util/regexes.ts | 73 ++- system-images/compat/Cargo.toml | 2 +- web/package-lock.json | 6 +- web/package.json | 2 +- .../download-doc/download-doc.component.html | 2 +- .../src/app/pages/success/success.page.ts | 8 +- .../src/app/services/api/mock-api.service.ts | 4 +- .../shared/src/types/workspace-config.ts | 2 +- .../app/app/preloader/preloader.component.ts | 2 +- .../form-select/form-select.component.html | 2 +- .../interface-info.component.html | 62 +++ .../interface-info.component.scss | 3 + .../interface-info.component.ts | 393 ++++++++++++++++ .../interface-info/interface-info.module.ts | 11 + .../app-interfaces-item.component.html | 44 -- .../app-interfaces/app-interfaces.module.ts | 10 +- .../app-interfaces/app-interfaces.page.html | 15 +- .../app-interfaces/app-interfaces.page.scss | 3 - .../app-interfaces/app-interfaces.page.ts | 145 +----- .../pages/server-routes/acme/acme.module.ts | 18 + .../pages/server-routes/acme/acme.page.html | 56 +++ .../pages/server-routes/acme/acme.page.scss | 0 .../app/pages/server-routes/acme/acme.page.ts | 117 +++++ .../server-routes/server-routing.module.ts | 5 + .../server-show/server-show.page.ts | 9 + .../server-specs/server-specs.module.ts | 2 + .../server-specs/server-specs.page.html | 71 +-- .../server-specs/server-specs.page.ts | 50 +- .../ui/src/app/services/api/api.fixures.ts | 51 ++- .../ui/src/app/services/api/api.types.ts | 74 +++ .../app/services/api/embassy-api.service.ts | 44 ++ .../services/api/embassy-live-api.service.ts | 112 +++++ .../services/api/embassy-mock-api.service.ts | 289 +++++++++++- .../ui/src/app/services/api/mock-patch.ts | 171 ++++++- .../ui/src/app/services/config.service.ts | 210 +++++++-- .../ui/src/app/services/form.service.ts | 2 +- web/projects/ui/src/app/util/acme.ts | 21 + 90 files changed, 2836 insertions(+), 1201 deletions(-) create mode 100644 core/startos/src/version/v0_3_6_alpha_12.rs delete mode 100644 package-lock.json delete mode 100644 sdk/base/lib/osBindings/HostKind.ts create mode 100644 web/projects/ui/src/app/components/interface-info/interface-info.component.html create mode 100644 web/projects/ui/src/app/components/interface-info/interface-info.component.scss create mode 100644 web/projects/ui/src/app/components/interface-info/interface-info.component.ts create mode 100644 web/projects/ui/src/app/components/interface-info/interface-info.module.ts delete mode 100644 web/projects/ui/src/app/pages/apps-routes/app-interfaces/app-interfaces-item.component.html create mode 100644 web/projects/ui/src/app/pages/server-routes/acme/acme.module.ts create mode 100644 web/projects/ui/src/app/pages/server-routes/acme/acme.page.html create mode 100644 web/projects/ui/src/app/pages/server-routes/acme/acme.page.scss create mode 100644 web/projects/ui/src/app/pages/server-routes/acme/acme.page.ts create mode 100644 web/projects/ui/src/app/util/acme.ts diff --git a/container-runtime/src/Adapters/Systems/SystemForEmbassy/MainLoop.ts b/container-runtime/src/Adapters/Systems/SystemForEmbassy/MainLoop.ts index b6fe39854..fa76a1f84 100644 --- a/container-runtime/src/Adapters/Systems/SystemForEmbassy/MainLoop.ts +++ b/container-runtime/src/Adapters/Systems/SystemForEmbassy/MainLoop.ts @@ -113,7 +113,6 @@ export class MainLoop { })) .find((conf) => conf.internal == internalPort) await effects.bind({ - kind: "multi", id: interfaceId, internalPort, preferredExternalPort: torConf?.external || internalPort, diff --git a/container-runtime/src/Adapters/Systems/SystemForEmbassy/index.ts b/container-runtime/src/Adapters/Systems/SystemForEmbassy/index.ts index e74ef317d..bc2da8871 100644 --- a/container-runtime/src/Adapters/Systems/SystemForEmbassy/index.ts +++ b/container-runtime/src/Adapters/Systems/SystemForEmbassy/index.ts @@ -401,6 +401,7 @@ export class SystemForEmbassy implements System { return [ port, { + protocol: null, secure: null, preferredExternalPort: Number.parseInt( torPort || lanPort || String(port), diff --git a/core/Cargo.lock b/core/Cargo.lock index bc489c481..3810d0d8d 100644 --- a/core/Cargo.lock +++ b/core/Cargo.lock @@ -163,11 +163,12 @@ dependencies = [ [[package]] name = "anstyle-wincon" -version = "3.0.6" +version = "3.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" +checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" dependencies = [ "anstyle", + "once_cell", "windows-sys 0.59.0", ] @@ -237,7 +238,7 @@ checksum = "965c2d33e53cb6b267e148a4cb0760bc01f4904c1cd4bb4002a085bb016d1490" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", "synstructure", ] @@ -249,7 +250,7 @@ checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -265,7 +266,7 @@ dependencies = [ "pem", "rcgen", "ring 0.17.8", - "rustls 0.23.20", + "rustls 0.23.21", "rustls-pemfile 2.2.0", "serde", "serde_json", @@ -404,7 +405,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -444,7 +445,7 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -461,7 +462,7 @@ checksum = "3f934833b4b7233644e5848f235df3f57ed8c80f1528a26c3dfa13d2147fa056" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -487,9 +488,9 @@ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "aws-lc-rs" -version = "1.12.0" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f409eb70b561706bf8abba8ca9c112729c481595893fd06a2dd9af8ed8441148" +checksum = "1ea835662a0af02443aa1396d39be523bbf8f11ee6fad20329607c480bea48c3" dependencies = [ "aws-lc-sys", "paste", @@ -498,9 +499,9 @@ dependencies = [ [[package]] name = "aws-lc-sys" -version = "0.24.1" +version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "923ded50f602b3007e5e63e3f094c479d9c8a9b42d7f4034e4afe456aa48bfd2" +checksum = "71b2ddd3ada61a305e1d8bb6c005d1eaa7d14d903681edfc400406d523a9b491" dependencies = [ "bindgen", "cc", @@ -717,7 +718,7 @@ version = "0.69.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "cexpr", "clang-sys", "itertools 0.12.1", @@ -730,7 +731,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn 2.0.95", + "syn 2.0.96", "which", ] @@ -772,9 +773,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.6.0" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" dependencies = [ "serde", ] @@ -946,9 +947,9 @@ checksum = "981520c98f422fcc584dc1a95c334e6953900b9106bc47a9839b81790009eb21" [[package]] name = "cc" -version = "1.2.7" +version = "1.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a012a0df96dd6d06ba9a1b29d6402d1a5d77c6befd2566afdc26e10603dc93d7" +checksum = "13208fcbb66eaeffe09b99fffbe1af420f00a7b35aa99ad683dfc1aa76145229" dependencies = [ "jobserver", "libc", @@ -1088,7 +1089,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -1382,7 +1383,7 @@ version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "crossterm_winapi", "futures-core", "mio", @@ -1497,7 +1498,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -1521,7 +1522,7 @@ dependencies = [ "proc-macro2", "quote", "strsim 0.11.1", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -1532,14 +1533,14 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] name = "data-encoding" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" +checksum = "0e60eed09d8c01d3cee5b7d30acb059b76614c918fa0f992e0dd6eeb10daad6f" [[package]] name = "deflate64" @@ -1569,7 +1570,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -1606,7 +1607,7 @@ checksum = "8034092389675178f570469e6c3b0465d3d30b4505c294a6550db47f3c17ad18" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -1627,7 +1628,7 @@ checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -1640,7 +1641,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -1693,7 +1694,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -1882,14 +1883,14 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] name = "enumflags2" -version = "0.7.10" +version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d232db7f5956f3f14313dc2f87985c58bd2c695ce124c8cdd984e08e15ac133d" +checksum = "ba2f4b465f5318854c6f8dd686ede6c0a9dc67d4b1ac241cf0eb51521a309147" dependencies = [ "enumflags2_derive", "serde", @@ -1897,13 +1898,13 @@ dependencies = [ [[package]] name = "enumflags2_derive" -version = "0.7.10" +version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de0d48a183585823424a4ce1aa132d174a6a81bd540895822eb4c8373a8e49e8" +checksum = "fc4caf64a58d7a6d65ab00639b046ff54399a39f5f2554728895ace4b297cd79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -2065,7 +2066,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" dependencies = [ "crc32fast", - "miniz_oxide 0.8.2", + "miniz_oxide 0.8.3", ] [[package]] @@ -2197,9 +2198,9 @@ checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-lite" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cef40d21ae2c515b51041df9ed313ed21e572df340ea58a922a0aefe7e8891a1" +checksum = "f5edaec856126859abb19ed65f39e90fea3a9574b9707f13539acf4abf7eb532" dependencies = [ "fastrand", "futures-core", @@ -2216,7 +2217,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -2340,7 +2341,7 @@ version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8283e7331b8c93b9756e0cfdbcfb90312852f953c6faf9bf741e684cc3b6ad69" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "crc", "log", "uuid", @@ -2738,7 +2739,7 @@ dependencies = [ "http 1.2.0", "hyper 1.5.2", "hyper-util", - "rustls 0.23.20", + "rustls 0.23.21", "rustls-pki-types", "tokio", "tokio-rustls 0.26.1", @@ -2930,7 +2931,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -3017,6 +3018,18 @@ dependencies = [ name = "imbl-value" version = "0.1.1" source = "git+https://github.com/Start9Labs/imbl-value.git#1900943e17116def03bf00bff05cf12e54d810bc" +dependencies = [ + "imbl", + "serde", + "serde_json", + "yasi", +] + +[[package]] +name = "imbl-value" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b3431be119ebf79f4bd0ce420dfa66aaeffbc4688f79c796237dc1f3b2d6d71" dependencies = [ "imbl", "serde", @@ -3281,9 +3294,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.76" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" dependencies = [ "once_cell", "wasm-bindgen", @@ -3293,7 +3306,7 @@ dependencies = [ name = "json-patch" version = "0.2.7-alpha.0" dependencies = [ - "imbl-value", + "imbl-value 0.1.2", "json-ptr", "serde", "treediff", @@ -3304,7 +3317,7 @@ name = "json-ptr" version = "0.1.0" dependencies = [ "imbl", - "imbl-value", + "imbl-value 0.1.2", "serde", "thiserror 1.0.69", ] @@ -3314,7 +3327,7 @@ name = "jsonpath_lib" version = "0.3.0" source = "git+https://github.com/Start9Labs/jsonpath.git#1cacbd64afa2e1941a21fef06bad14317ba92f30" dependencies = [ - "imbl-value", + "imbl-value 0.1.1", "log", "serde", "serde_json", @@ -3432,7 +3445,7 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "libc", "redox_syscall 0.5.8", ] @@ -3496,9 +3509,9 @@ checksum = "9374ef4228402d4b7e403e5838cb880d9ee663314b0a900d5a6aabf0c213552e" [[package]] name = "log" -version = "0.4.22" +version = "0.4.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" [[package]] name = "lru-cache" @@ -3580,7 +3593,7 @@ dependencies = [ "mail-builder", "md5", "rand 0.8.5", - "rustls 0.23.20", + "rustls 0.23.21", "rustls-pki-types", "smtp-proto", "tokio", @@ -3703,9 +3716,9 @@ dependencies = [ [[package]] name = "miniz_oxide" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ffbe83022cedc1d264172192511ae958937694cd57ce297164951b8b3568394" +checksum = "b8402cab7aefae129c6977bb0ff1b8fd9a04eb5b51efc50a70bea51cda0c7924" dependencies = [ "adler2", ] @@ -3741,7 +3754,7 @@ dependencies = [ "regex", "reqwest", "rpc-toolkit", - "rustls 0.23.20", + "rustls 0.23.21", "serde", "serde_json", "sqlx", @@ -3819,7 +3832,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "cfg-if", "cfg_aliases", "libc", @@ -3993,7 +4006,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -4051,7 +4064,7 @@ version = "0.10.68" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6174bc48f102d208783c2c84bf931bb75927a617866870de8a4ea85597f871f5" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "cfg-if", "foreign-types", "libc", @@ -4068,7 +4081,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -4202,7 +4215,7 @@ dependencies = [ "fd-lock-rs", "futures", "imbl", - "imbl-value", + "imbl-value 0.1.2", "json-patch", "json-ptr", "lazy_static", @@ -4277,7 +4290,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b7cafe60d6cf8e62e1b9b2ea516a089c008945bb5a275416789e7db0bc199dc" dependencies = [ "memchr", - "thiserror 2.0.10", + "thiserror 2.0.11", "ucd-trie", ] @@ -4301,7 +4314,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -4357,7 +4370,7 @@ checksum = "d56a66c0c55993aa927429d0f8a0abfd74f084e4d9c192cffed01e418d83eefb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -4454,12 +4467,12 @@ checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" [[package]] name = "prettyplease" -version = "0.2.27" +version = "0.2.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "483f8c21f64f3ea09fe0f30f5d48c3e8eefe5dac9129f0075f76593b4c1da705" +checksum = "6924ced06e1f7dfe3fa48d57b9f74f55d8915f5036121bef647ef4b204895fac" dependencies = [ "proc-macro2", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -4496,9 +4509,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.92" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" +checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" dependencies = [ "unicode-ident", ] @@ -4509,7 +4522,7 @@ version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "731e0d9356b0c25f16f33b5be79b1c57b562f141ebfcdb0ad8ac2c13a24293b4" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "chrono", "flate2", "hex", @@ -4524,7 +4537,7 @@ version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d3554923a69f4ce04c4a754260c338f505ce22642d3830e049a399fc2059a29" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "chrono", "hex", ] @@ -4537,7 +4550,7 @@ checksum = "14cae93065090804185d3b75f0bf93b8eeda30c7a9b4a33d3bdb3988d6229e50" dependencies = [ "bit-set 0.8.0", "bit-vec 0.8.0", - "bitflags 2.6.0", + "bitflags 2.8.0", "lazy_static", "num-traits", "rand 0.8.5", @@ -4557,7 +4570,7 @@ checksum = "4ee1c9ac207483d5e7db4940700de86a9aae46ef90c48b57f99fe7edb8345e49" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -4580,7 +4593,7 @@ dependencies = [ "itertools 0.12.1", "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -4785,7 +4798,7 @@ version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", ] [[package]] @@ -4967,8 +4980,8 @@ dependencies = [ [[package]] name = "rpc-toolkit" -version = "0.2.3" -source = "git+https://github.com/Start9Labs/rpc-toolkit.git?branch=refactor%2Fno-dyn-ctx#21e35d85fb8f5de0e046c7ab5266236c2b639a4b" +version = "0.3.0" +source = "git+https://github.com/Start9Labs/rpc-toolkit.git?branch=master#0747acc54b3dafa8689db1365fba5b28a03806b1" dependencies = [ "async-stream", "async-trait", @@ -4977,7 +4990,7 @@ dependencies = [ "futures", "http 1.2.0", "http-body-util", - "imbl-value", + "imbl-value 0.1.2", "itertools 0.12.1", "lazy_format", "lazy_static", @@ -5071,7 +5084,7 @@ version = "0.38.43" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a78891ee6bf2340288408954ac787aa063d8e8817e9f53abb37c695c6d834ef6" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "errno 0.3.10", "libc", "linux-raw-sys", @@ -5106,9 +5119,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.20" +version = "0.23.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5065c3f250cbd332cd894be57c40fa52387247659b14a2d6041d121547903b1b" +checksum = "8f287924602bf649d949c63dc8ac8b235fa5387d394020705b80c4eb597ce5b8" dependencies = [ "aws-lc-rs", "log", @@ -5194,7 +5207,7 @@ dependencies = [ "futures-util", "pin-project", "thingbuf", - "thiserror 2.0.10", + "thiserror 2.0.11", "unicode-segmentation", "unicode-width 0.2.0", ] @@ -5259,7 +5272,7 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "core-foundation", "core-foundation-sys", "libc", @@ -5319,7 +5332,7 @@ checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -5364,7 +5377,7 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -5415,7 +5428,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -5745,7 +5758,7 @@ checksum = "1ed31390216d20e538e447a7a9b959e06ed9fc51c37b514b46eb758016ecd418" dependencies = [ "atoi", "base64 0.21.7", - "bitflags 2.6.0", + "bitflags 2.8.0", "byteorder", "bytes", "chrono", @@ -5788,7 +5801,7 @@ checksum = "7c824eb80b894f926f89a0b9da0c7f435d27cdd35b8c655b114e58223918577e" dependencies = [ "atoi", "base64 0.21.7", - "bitflags 2.6.0", + "bitflags 2.8.0", "byteorder", "chrono", "crc", @@ -5866,7 +5879,7 @@ dependencies = [ "quote", "regex-syntax 0.6.29", "strsim 0.10.0", - "syn 2.0.95", + "syn 2.0.96", "unicode-width 0.1.14", ] @@ -5920,7 +5933,7 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[package]] name = "start-os" -version = "0.3.6-alpha.11" +version = "0.3.6-alpha.12" dependencies = [ "aes 0.7.5", "async-acme", @@ -5965,7 +5978,7 @@ dependencies = [ "hyper-util", "id-pool", "imbl", - "imbl-value", + "imbl-value 0.1.2", "include_dir", "indexmap 2.7.0", "indicatif", @@ -6013,7 +6026,7 @@ dependencies = [ "rpassword", "rpc-toolkit", "rust-argon2", - "rustls 0.23.20", + "rustls 0.23.21", "rustls-pki-types", "rustyline-async", "semver", @@ -6121,9 +6134,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.95" +version = "2.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46f71c0377baf4ef1cc3e3402ded576dccc315800fbc62dfc7fe04b009773b4a" +checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80" dependencies = [ "proc-macro2", "quote", @@ -6153,7 +6166,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -6162,7 +6175,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "core-foundation", "system-configuration-sys", ] @@ -6260,11 +6273,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.10" +version = "2.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3ac7f54ca534db81081ef1c1e7f6ea8a3ef428d2fc069097c079443d24124d3" +checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc" dependencies = [ - "thiserror-impl 2.0.10", + "thiserror-impl 2.0.11", ] [[package]] @@ -6275,18 +6288,18 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] name = "thiserror-impl" -version = "2.0.10" +version = "2.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e9465d30713b56a37ede7185763c3492a91be2f5fa68d958c44e41ab9248beb" +checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -6412,7 +6425,7 @@ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -6452,7 +6465,7 @@ version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f6d0975eaace0cf0fcadee4e4aaa5da15b5c079146f2cffb67c113be122bf37" dependencies = [ - "rustls 0.23.20", + "rustls 0.23.21", "tokio", ] @@ -6589,7 +6602,7 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime", - "winnow 0.6.22", + "winnow 0.6.24", ] [[package]] @@ -6706,7 +6719,7 @@ checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -6858,7 +6871,7 @@ dependencies = [ "Inflector", "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", "termcolor", ] @@ -6928,7 +6941,7 @@ checksum = "1f718dfaf347dcb5b983bfc87608144b0bad87970aebcbea5ce44d2a30c08e63" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -7095,18 +7108,18 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" +checksum = "744018581f9a3454a9e15beb8a33b017183f1e7c0cd170232a2d1453b23a51c4" dependencies = [ "getrandom 0.2.15", ] [[package]] name = "valuable" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" [[package]] name = "vcpkg" @@ -7168,34 +7181,35 @@ checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" [[package]] name = "wasm-bindgen" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" dependencies = [ "cfg-if", "once_cell", + "rustversion", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" dependencies = [ "bumpalo", "log", "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.49" +version = "0.4.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38176d9b44ea84e9184eff0bc34cc167ed044f816accfe5922e54d84cf48eca2" +checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" dependencies = [ "cfg-if", "js-sys", @@ -7206,9 +7220,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -7216,22 +7230,25 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] [[package]] name = "wasm-streams" @@ -7248,9 +7265,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.76" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04dd7223427d52553d3702c004d3b2fe07c148165faa56313cb00211e31c12bc" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" dependencies = [ "js-sys", "wasm-bindgen", @@ -7538,9 +7555,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.6.22" +version = "0.6.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39281189af81c07ec09db316b302a3e67bf9bd7cbf6c820b50e35fee9c2fa980" +checksum = "c8d71a593cc5c42ad7876e2c1fda56f314f3754c084128833e64f1345ff8a03a" dependencies = [ "memchr", ] @@ -7691,15 +7708,15 @@ checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", "synstructure", ] [[package]] name = "zbus" -version = "5.2.0" +version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb67eadba43784b6fb14857eba0d8fc518686d3ee537066eb6086dc318e2c8a1" +checksum = "192a0d989036cd60a1e91a54c9851fb9ad5bd96125d41803eed79d2e2ef74bd7" dependencies = [ "async-broadcast", "async-executor", @@ -7724,7 +7741,7 @@ dependencies = [ "tracing", "uds_windows", "windows-sys 0.59.0", - "winnow 0.6.22", + "winnow 0.6.24", "xdg-home", "zbus_macros", "zbus_names", @@ -7733,14 +7750,14 @@ dependencies = [ [[package]] name = "zbus_macros" -version = "5.2.0" +version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c9d49ebc960ceb660f2abe40a5904da975de6986f2af0d7884b39eec6528c57" +checksum = "3685b5c81fce630efc3e143a4ded235b107f1b1cdf186c3f115529e5e5ae4265" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", "zbus_names", "zvariant", "zvariant_utils", @@ -7748,13 +7765,13 @@ dependencies = [ [[package]] name = "zbus_names" -version = "4.1.0" +version = "4.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "856b7a38811f71846fd47856ceee8bccaec8399ff53fb370247e66081ace647b" +checksum = "519629a3f80976d89c575895b05677cbc45eaf9f70d62a364d819ba646409cc8" dependencies = [ "serde", "static_assertions", - "winnow 0.6.22", + "winnow 0.6.24", "zvariant", ] @@ -7776,7 +7793,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -7796,7 +7813,7 @@ checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", "synstructure", ] @@ -7817,7 +7834,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -7839,7 +7856,7 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", ] [[package]] @@ -7864,7 +7881,7 @@ dependencies = [ "pbkdf2", "rand 0.8.5", "sha1", - "thiserror 2.0.10", + "thiserror 2.0.11", "time", "zeroize", "zopfli", @@ -7915,42 +7932,42 @@ dependencies = [ [[package]] name = "zvariant" -version = "5.1.0" +version = "5.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1200ee6ac32f1e5a312e455a949a4794855515d34f9909f4a3e082d14e1a56f" +checksum = "55e6b9b5f1361de2d5e7d9fd1ee5f6f7fcb6060618a1f82f3472f58f2b8d4be9" dependencies = [ "endi", "enumflags2", "serde", "static_assertions", - "winnow 0.6.22", + "winnow 0.6.24", "zvariant_derive", "zvariant_utils", ] [[package]] name = "zvariant_derive" -version = "5.1.0" +version = "5.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "687e3b97fae6c9104fbbd36c73d27d149abf04fb874e2efbd84838763daa8916" +checksum = "573a8dd76961957108b10f7a45bac6ab1ea3e9b7fe01aff88325dc57bb8f5c8b" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.95", + "syn 2.0.96", "zvariant_utils", ] [[package]] name = "zvariant_utils" -version = "3.0.2" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20d1d011a38f12360e5fcccceeff5e2c42a8eb7f27f0dcba97a0862ede05c9c6" +checksum = "ddd46446ea2a1f353bfda53e35f17633afa79f4fe290a611c94645c69fe96a50" dependencies = [ "proc-macro2", "quote", "serde", "static_assertions", - "syn 2.0.95", - "winnow 0.6.22", + "syn 2.0.96", + "winnow 0.6.24", ] diff --git a/core/helpers/Cargo.toml b/core/helpers/Cargo.toml index 9af19018e..caf4b3eef 100644 --- a/core/helpers/Cargo.toml +++ b/core/helpers/Cargo.toml @@ -11,7 +11,7 @@ futures = "0.3.28" lazy_async_pool = "0.3.3" models = { path = "../models" } pin-project = "1.1.3" -rpc-toolkit = { git = "https://github.com/Start9Labs/rpc-toolkit.git", branch = "refactor/no-dyn-ctx" } +rpc-toolkit = { git = "https://github.com/Start9Labs/rpc-toolkit.git", branch = "master" } serde = { version = "1.0", features = ["derive", "rc"] } serde_json = "1.0" tokio = { version = "1", features = ["full"] } diff --git a/core/models/Cargo.toml b/core/models/Cargo.toml index 5defd1986..9e216bc60 100644 --- a/core/models/Cargo.toml +++ b/core/models/Cargo.toml @@ -24,7 +24,7 @@ patch-db = { version = "*", path = "../../patch-db/patch-db", features = [ rand = "0.8.5" regex = "1.10.2" reqwest = "0.12" -rpc-toolkit = { git = "https://github.com/Start9Labs/rpc-toolkit.git", branch = "refactor/no-dyn-ctx" } +rpc-toolkit = { git = "https://github.com/Start9Labs/rpc-toolkit.git", branch = "master" } rustls = "0.23" serde = { version = "1.0", features = ["derive", "rc"] } serde_json = "1.0" diff --git a/core/models/src/data_url.rs b/core/models/src/data_url.rs index 9757d7f6a..8a95c993e 100644 --- a/core/models/src/data_url.rs +++ b/core/models/src/data_url.rs @@ -168,6 +168,6 @@ fn doesnt_reallocate() { mime: InternedString::intern("png"), data: Cow::Borrowed(&random[..i]), }; - assert_eq!(dbg!(icon.to_string()).capacity(), icon.data_url_len()); + assert_eq!(icon.to_string().capacity(), icon.data_url_len()); } } diff --git a/core/models/src/errors.rs b/core/models/src/errors.rs index e7b73b0ec..21ff5072b 100644 --- a/core/models/src/errors.rs +++ b/core/models/src/errors.rs @@ -45,7 +45,7 @@ pub enum ErrorKind { ConfigGen = 27, ParseNumber = 28, Database = 29, - InvalidPackageId = 30, + InvalidId = 30, InvalidSignature = 31, Backup = 32, Restore = 33, @@ -125,7 +125,7 @@ impl ErrorKind { ConfigGen => "Config Generation Error", ParseNumber => "Number Parsing Error", Database => "Database Error", - InvalidPackageId => "Invalid Package ID", + InvalidId => "Invalid ID", InvalidSignature => "Invalid Signature", Backup => "Backup Error", Restore => "Restore Error", @@ -226,7 +226,7 @@ impl From for Error { } impl From for Error { fn from(err: InvalidId) -> Self { - Error::new(err, ErrorKind::InvalidPackageId) + Error::new(err, ErrorKind::InvalidId) } } impl From for Error { diff --git a/core/models/src/id/invalid_id.rs b/core/models/src/id/invalid_id.rs index d2cc82bd5..4a6f0f2e7 100644 --- a/core/models/src/id/invalid_id.rs +++ b/core/models/src/id/invalid_id.rs @@ -1,3 +1,5 @@ +use yasi::InternedString; + #[derive(Debug, thiserror::Error)] -#[error("Invalid ID")] -pub struct InvalidId; +#[error("Invalid ID: {0}")] +pub struct InvalidId(pub(super) InternedString); diff --git a/core/models/src/id/mod.rs b/core/models/src/id/mod.rs index 90dbc978c..0c313973f 100644 --- a/core/models/src/id/mod.rs +++ b/core/models/src/id/mod.rs @@ -43,7 +43,7 @@ impl TryFrom for Id { if ID_REGEX.is_match(&value) { Ok(Id(value)) } else { - Err(InvalidId) + Err(InvalidId(value)) } } } @@ -53,7 +53,7 @@ impl TryFrom for Id { if ID_REGEX.is_match(&value) { Ok(Id(InternedString::intern(value))) } else { - Err(InvalidId) + Err(InvalidId(InternedString::intern(value))) } } } @@ -63,7 +63,7 @@ impl TryFrom<&str> for Id { if ID_REGEX.is_match(value) { Ok(Id(InternedString::intern(value))) } else { - Err(InvalidId) + Err(InvalidId(InternedString::intern(value))) } } } diff --git a/core/startos/Cargo.toml b/core/startos/Cargo.toml index 62453460a..909389c12 100644 --- a/core/startos/Cargo.toml +++ b/core/startos/Cargo.toml @@ -14,7 +14,7 @@ keywords = [ name = "start-os" readme = "README.md" repository = "https://github.com/Start9Labs/start-os" -version = "0.3.6-alpha.11" +version = "0.3.6-alpha.12" license = "MIT" [lib] @@ -117,7 +117,7 @@ id-pool = { version = "0.2.2", default-features = false, features = [ "u16", ] } imbl = "2.0.3" -imbl-value = { git = "https://github.com/Start9Labs/imbl-value.git" } +imbl-value = "0.1.2" include_dir = { version = "0.7.3", features = ["metadata"] } indexmap = { version = "2.0.2", features = ["serde"] } indicatif = { version = "0.17.7", features = ["tokio"] } @@ -172,7 +172,7 @@ regex = "1.10.2" reqwest = { version = "0.12.4", features = ["stream", "json", "socks"] } reqwest_cookie_store = "0.8.0" rpassword = "7.2.0" -rpc-toolkit = { git = "https://github.com/Start9Labs/rpc-toolkit.git", branch = "refactor/no-dyn-ctx" } +rpc-toolkit = { git = "https://github.com/Start9Labs/rpc-toolkit.git", branch = "master" } rust-argon2 = "2.0.0" rustyline-async = "0.4.1" semver = { version = "1.0.20", features = ["serde"] } diff --git a/core/startos/src/account.rs b/core/startos/src/account.rs index 9e755342f..389589667 100644 --- a/core/startos/src/account.rs +++ b/core/startos/src/account.rs @@ -24,7 +24,7 @@ pub struct AccountInfo { pub server_id: String, pub hostname: Hostname, pub password: String, - pub tor_key: TorSecretKeyV3, + pub tor_keys: Vec, pub root_ca_key: PKey, pub root_ca_cert: X509, pub ssh_key: ssh_key::PrivateKey, @@ -34,7 +34,7 @@ impl AccountInfo { pub fn new(password: &str, start_time: SystemTime) -> Result { let server_id = generate_id(); let hostname = generate_hostname(); - let tor_key = TorSecretKeyV3::generate(); + let tor_key = vec![TorSecretKeyV3::generate()]; let root_ca_key = generate_key()?; let root_ca_cert = make_root_cert(&root_ca_key, &hostname, start_time)?; let ssh_key = ssh_key::PrivateKey::from(ssh_key::private::Ed25519Keypair::random( @@ -45,7 +45,7 @@ impl AccountInfo { server_id, hostname, password: hash_password(password)?, - tor_key, + tor_keys: tor_key, root_ca_key, root_ca_cert, ssh_key, @@ -58,8 +58,11 @@ impl AccountInfo { let hostname = Hostname(db.as_public().as_server_info().as_hostname().de()?); let password = db.as_private().as_password().de()?; let key_store = db.as_private().as_key_store(); - let tor_addr = db.as_public().as_server_info().as_onion_address().de()?; - let tor_key = key_store.as_onion().get_key(&tor_addr)?; + let tor_addrs = db.as_public().as_server_info().as_host().as_onions().de()?; + let tor_keys = tor_addrs + .into_iter() + .map(|tor_addr| key_store.as_onion().get_key(&tor_addr)) + .collect::>()?; let cert_store = key_store.as_local_certs(); let root_ca_key = cert_store.as_root_key().de()?.0; let root_ca_cert = cert_store.as_root_cert().de()?.0; @@ -70,7 +73,7 @@ impl AccountInfo { server_id, hostname, password, - tor_key, + tor_keys, root_ca_key, root_ca_cert, ssh_key, @@ -82,17 +85,16 @@ impl AccountInfo { let server_info = db.as_public_mut().as_server_info_mut(); server_info.as_id_mut().ser(&self.server_id)?; server_info.as_hostname_mut().ser(&self.hostname.0)?; - server_info - .as_lan_address_mut() - .ser(&self.hostname.lan_address().parse()?)?; server_info .as_pubkey_mut() .ser(&self.ssh_key.public_key().to_openssh()?)?; - let onion_address = self.tor_key.public().get_onion_address(); - server_info.as_onion_address_mut().ser(&onion_address)?; - server_info - .as_tor_address_mut() - .ser(&format!("https://{onion_address}").parse()?)?; + server_info.as_host_mut().as_onions_mut().ser( + &self + .tor_keys + .iter() + .map(|tor_key| tor_key.public().get_onion_address()) + .collect(), + )?; db.as_private_mut().as_password_mut().ser(&self.password)?; db.as_private_mut() .as_ssh_privkey_mut() @@ -101,7 +103,9 @@ impl AccountInfo { .as_compat_s9pk_key_mut() .ser(Pem::new_ref(&self.compat_s9pk_key))?; let key_store = db.as_private_mut().as_key_store_mut(); - key_store.as_onion_mut().insert_key(&self.tor_key)?; + for tor_key in &self.tor_keys { + key_store.as_onion_mut().insert_key(tor_key)?; + } let cert_store = key_store.as_local_certs_mut(); cert_store .as_root_key_mut() diff --git a/core/startos/src/backup/os.rs b/core/startos/src/backup/os.rs index 6f08c5f43..324543fb8 100644 --- a/core/startos/src/backup/os.rs +++ b/core/startos/src/backup/os.rs @@ -85,7 +85,7 @@ impl OsBackupV0 { &mut rand::thread_rng(), ssh_key::Algorithm::Ed25519, )?, - tor_key: TorSecretKeyV3::from(self.tor_key.0), + tor_keys: vec![TorSecretKeyV3::from(self.tor_key.0)], compat_s9pk_key: ed25519_dalek::SigningKey::generate(&mut rand::thread_rng()), }, ui: self.ui, @@ -114,7 +114,7 @@ impl OsBackupV1 { root_ca_key: self.root_ca_key.0, root_ca_cert: self.root_ca_cert.0, ssh_key: ssh_key::PrivateKey::from(Ed25519Keypair::from_seed(&self.net_key.0)), - tor_key: TorSecretKeyV3::from(ed25519_expand_key(&self.net_key.0)), + tor_keys: vec![TorSecretKeyV3::from(ed25519_expand_key(&self.net_key.0))], compat_s9pk_key: ed25519_dalek::SigningKey::from_bytes(&self.net_key), }, ui: self.ui, @@ -132,7 +132,7 @@ struct OsBackupV2 { root_ca_key: Pem>, // PEM Encoded OpenSSL Key root_ca_cert: Pem, // PEM Encoded OpenSSL X509 Certificate ssh_key: Pem, // PEM Encoded OpenSSH Key - tor_key: TorSecretKeyV3, // Base64 Encoded Ed25519 Expanded Secret Key + tor_keys: Vec, // Base64 Encoded Ed25519 Expanded Secret Key compat_s9pk_key: Pem, // PEM Encoded ED25519 Key ui: Value, // JSON Value } @@ -146,7 +146,7 @@ impl OsBackupV2 { root_ca_key: self.root_ca_key.0, root_ca_cert: self.root_ca_cert.0, ssh_key: self.ssh_key.0, - tor_key: self.tor_key, + tor_keys: self.tor_keys, compat_s9pk_key: self.compat_s9pk_key.0, }, ui: self.ui, @@ -159,7 +159,7 @@ impl OsBackupV2 { root_ca_key: Pem(backup.account.root_ca_key.clone()), root_ca_cert: Pem(backup.account.root_ca_cert.clone()), ssh_key: Pem(backup.account.ssh_key.clone()), - tor_key: backup.account.tor_key.clone(), + tor_keys: backup.account.tor_keys.clone(), compat_s9pk_key: Pem(backup.account.compat_s9pk_key.clone()), ui: backup.ui.clone(), } diff --git a/core/startos/src/backup/restore.rs b/core/startos/src/backup/restore.rs index e2e9e2158..21f154b60 100644 --- a/core/startos/src/backup/restore.rs +++ b/core/startos/src/backup/restore.rs @@ -18,7 +18,7 @@ use crate::db::model::Database; use crate::disk::mount::backup::BackupMountGuard; use crate::disk::mount::filesystem::ReadWrite; use crate::disk::mount::guard::{GenericMountGuard, TmpMountGuard}; -use crate::init::{init, InitResult}; +use crate::init::init; use crate::prelude::*; use crate::s9pk::S9pk; use crate::service::service_map::DownloadInstallFuture; @@ -109,13 +109,13 @@ pub async fn recover_full_embassy( db.put(&ROOT, &Database::init(&os_backup.account)?).await?; drop(db); - let InitResult { net_ctrl } = init(&ctx.webserver, &ctx.config, init_phases).await?; + let init_result = init(&ctx.webserver, &ctx.config, init_phases).await?; let rpc_ctx = RpcContext::init( &ctx.webserver, &ctx.config, disk_guid.clone(), - Some(net_ctrl), + Some(init_result), rpc_ctx_phases, ) .await?; diff --git a/core/startos/src/bins/start_init.rs b/core/startos/src/bins/start_init.rs index 7ff903090..fdd0e075d 100644 --- a/core/startos/src/bins/start_init.rs +++ b/core/startos/src/bins/start_init.rs @@ -11,7 +11,7 @@ use crate::disk::fsck::RepairStrategy; use crate::disk::main::DEFAULT_PASSWORD; use crate::disk::REPAIR_DISK_PATH; use crate::firmware::{check_for_firmware_update, update_firmware}; -use crate::init::{InitPhases, InitResult, STANDBY_MODE_PATH}; +use crate::init::{InitPhases, STANDBY_MODE_PATH}; use crate::net::web_server::{UpgradableListener, WebServer}; use crate::prelude::*; use crate::progress::FullProgressTracker; @@ -188,14 +188,14 @@ async fn setup_or_init( })); } - let InitResult { net_ctrl } = + let init_result = crate::init::init(&server.acceptor_setter(), config, init_phases).await?; let rpc_ctx = RpcContext::init( &server.acceptor_setter(), config, disk_guid, - Some(net_ctrl), + Some(init_result), rpc_ctx_phases, ) .await?; diff --git a/core/startos/src/context/config.rs b/core/startos/src/context/config.rs index 3f631c1e4..4a0925d81 100644 --- a/core/startos/src/context/config.rs +++ b/core/startos/src/context/config.rs @@ -108,8 +108,6 @@ pub struct ServerConfig { #[arg(long)] pub tor_socks: Option, #[arg(long)] - pub dns_bind: Option>, - #[arg(long)] pub revision_cache_size: Option, #[arg(long)] pub disable_encryption: Option, @@ -125,7 +123,6 @@ impl ContextConfig for ServerConfig { self.os_partitions = self.os_partitions.take().or(other.os_partitions); self.tor_control = self.tor_control.take().or(other.tor_control); self.tor_socks = self.tor_socks.take().or(other.tor_socks); - self.dns_bind = self.dns_bind.take().or(other.dns_bind); self.revision_cache_size = self .revision_cache_size .take() diff --git a/core/startos/src/context/rpc.rs b/core/startos/src/context/rpc.rs index a00b9d681..1eca857f4 100644 --- a/core/startos/src/context/rpc.rs +++ b/core/startos/src/context/rpc.rs @@ -26,9 +26,9 @@ use crate::auth::Sessions; use crate::context::config::ServerConfig; use crate::db::model::Database; use crate::disk::OsPartitionInfo; -use crate::init::check_time_is_synchronized; +use crate::init::{check_time_is_synchronized, InitResult}; use crate::lxc::{ContainerId, LxcContainer, LxcManager}; -use crate::net::net_controller::{NetController, PreInitNetController}; +use crate::net::net_controller::{NetController, NetService}; use crate::net::utils::{find_eth_iface, find_wifi_iface}; use crate::net::web_server::{UpgradableListener, WebServerAcceptorSetter}; use crate::net::wifi::WpaCli; @@ -53,6 +53,7 @@ pub struct RpcContextSeed { pub sync_db: watch::Sender, pub account: RwLock, pub net_controller: Arc, + pub os_net_service: NetService, pub s9pk_arch: Option<&'static str>, pub services: ServiceMap, pub metrics_cache: RwLock>, @@ -119,7 +120,7 @@ impl RpcContext { webserver: &WebServerAcceptorSetter, config: &ServerConfig, disk_guid: Arc, - net_ctrl: Option, + init_result: Option, InitRpcContextPhases { mut load_db, mut init_net_ctrl, @@ -133,7 +134,7 @@ impl RpcContext { let (shutdown, _) = tokio::sync::broadcast::channel(1); load_db.start(); - let db = if let Some(net_ctrl) = &net_ctrl { + let db = if let Some(InitResult { net_ctrl, .. }) = &init_result { net_ctrl.db.clone() } else { TypedPatchDb::::load(config.db().await?).await? @@ -144,31 +145,28 @@ impl RpcContext { tracing::info!("Opened PatchDB"); init_net_ctrl.start(); - let net_controller = Arc::new( - NetController::init( - if let Some(net_ctrl) = net_ctrl { - net_ctrl - } else { - let net_ctrl = PreInitNetController::init( - db.clone(), - config - .tor_control - .unwrap_or(SocketAddr::from(([127, 0, 0, 1], 9051))), - tor_proxy, - &account.hostname, - account.tor_key.clone(), - ) - .await?; - webserver.try_upgrade(|a| net_ctrl.net_iface.upgrade_listener(a))?; - net_ctrl - }, - config - .dns_bind - .as_deref() - .unwrap_or(&[SocketAddr::from(([127, 0, 0, 1], 53))]), - ) - .await?, - ); + let (net_controller, os_net_service) = if let Some(InitResult { + net_ctrl, + os_net_service, + }) = init_result + { + (net_ctrl, os_net_service) + } else { + let net_ctrl = Arc::new( + NetController::init( + db.clone(), + config + .tor_control + .unwrap_or(SocketAddr::from(([127, 0, 0, 1], 9051))), + tor_proxy, + &account.hostname, + ) + .await?, + ); + webserver.try_upgrade(|a| net_ctrl.net_iface.upgrade_listener(a))?; + let os_net_service = net_ctrl.os_bindings().await?; + (net_ctrl, os_net_service) + }; init_net_ctrl.complete(); tracing::info!("Initialized Net Controller"); @@ -230,6 +228,7 @@ impl RpcContext { db, account: RwLock::new(account), net_controller, + os_net_service, s9pk_arch: if config.multi_arch_s9pks.unwrap_or(false) { None } else { diff --git a/core/startos/src/context/setup.rs b/core/startos/src/context/setup.rs index 5c2bf8bfc..b8e4ee968 100644 --- a/core/startos/src/context/setup.rs +++ b/core/startos/src/context/setup.rs @@ -40,7 +40,7 @@ lazy_static::lazy_static! { #[serde(rename_all = "camelCase")] #[ts(export)] pub struct SetupResult { - pub tor_address: String, + pub tor_addresses: Vec, #[ts(type = "string")] pub hostname: Hostname, #[ts(type = "string")] @@ -51,7 +51,11 @@ impl TryFrom<&AccountInfo> for SetupResult { type Error = Error; fn try_from(value: &AccountInfo) -> Result { Ok(Self { - tor_address: format!("https://{}", value.tor_key.public().get_onion_address()), + tor_addresses: value + .tor_keys + .iter() + .map(|tor_key| format!("https://{}", tor_key.public().get_onion_address())) + .collect(), hostname: value.hostname.clone(), lan_address: value.hostname.lan_address(), root_ca: String::from_utf8(value.root_ca_cert.to_pem()?)?, diff --git a/core/startos/src/db/model/public.rs b/core/startos/src/db/model/public.rs index 90df469ba..bb92bbaf9 100644 --- a/core/startos/src/db/model/public.rs +++ b/core/startos/src/db/model/public.rs @@ -10,19 +10,22 @@ use itertools::Itertools; use models::PackageId; use openssl::hash::MessageDigest; use patch_db::{HasModel, Value}; -use reqwest::Url; use serde::{Deserialize, Serialize}; -use torut::onion::OnionAddressV3; use ts_rs::TS; use crate::account::AccountInfo; use crate::db::model::package::AllPackageData; use crate::net::acme::AcmeProvider; +use crate::net::host::address::DomainConfig; +use crate::net::host::binding::{AddSslOptions, BindInfo, BindOptions, NetInfo}; +use crate::net::host::Host; +use crate::net::vhost::AlpnInfo; use crate::prelude::*; use crate::progress::FullProgress; use crate::system::SmtpValue; use crate::util::cpupower::Governor; use crate::util::lshw::LshwDevice; +use crate::util::serde::MaybeUtf8String; use crate::version::{Current, VersionT}; use crate::{ARCH, PLATFORM}; @@ -38,7 +41,6 @@ pub struct Public { } impl Public { pub fn init(account: &AccountInfo) -> Result { - let lan_address = account.hostname.lan_address().parse().unwrap(); Ok(Self { server_info: ServerInfo { arch: get_arch(), @@ -46,14 +48,42 @@ impl Public { id: account.server_id.clone(), version: Current::default().semver(), hostname: account.hostname.no_dot_host_name(), + host: Host { + bindings: [( + 80, + BindInfo { + enabled: false, + options: BindOptions { + preferred_external_port: 80, + add_ssl: Some(AddSslOptions { + preferred_external_port: 443, + alpn: Some(AlpnInfo::Specified(vec![ + MaybeUtf8String("http/1.1".into()), + MaybeUtf8String("h2".into()), + ])), + }), + secure: None, + }, + net: NetInfo { + assigned_port: None, + assigned_ssl_port: Some(443), + public: false, + }, + }, + )] + .into_iter() + .collect(), + onions: account + .tor_keys + .iter() + .map(|k| k.public().get_onion_address()) + .collect(), + domains: BTreeMap::new(), + hostname_info: BTreeMap::new(), + }, last_backup: None, package_version_compat: Current::default().compat().clone(), post_init_migration_todos: BTreeSet::new(), - lan_address, - onion_address: account.tor_key.public().get_onion_address(), - tor_address: format!("https://{}", account.tor_key.public().get_onion_address()) - .parse() - .unwrap(), network_interfaces: BTreeMap::new(), acme: BTreeMap::new(), status_info: ServerStatus { @@ -115,6 +145,7 @@ pub struct ServerInfo { pub id: String, #[ts(type = "string")] pub hostname: InternedString, + pub host: Host, #[ts(type = "string")] pub version: Version, #[ts(type = "string")] @@ -123,13 +154,6 @@ pub struct ServerInfo { pub post_init_migration_todos: BTreeSet, #[ts(type = "string | null")] pub last_backup: Option>, - #[ts(type = "string")] - pub lan_address: Url, - #[ts(type = "string")] - pub onion_address: OnionAddressV3, - /// for backwards compatibility - #[ts(type = "string")] - pub tor_address: Url, #[ts(as = "BTreeMap::")] #[serde(default)] pub network_interfaces: BTreeMap, diff --git a/core/startos/src/init.rs b/core/startos/src/init.rs index 21d23ab5a..b68596a12 100644 --- a/core/startos/src/init.rs +++ b/core/startos/src/init.rs @@ -3,6 +3,7 @@ use std::io::Cursor; use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4}; use std::os::unix::fs::PermissionsExt; use std::path::Path; +use std::sync::Arc; use std::time::{Duration, SystemTime}; use axum::extract::ws::{self}; @@ -25,7 +26,8 @@ use crate::db::model::public::ServerStatus; use crate::db::model::Database; use crate::disk::mount::util::unmount; use crate::middleware::auth::LOCAL_AUTH_COOKIE_PATH; -use crate::net::net_controller::PreInitNetController; +use crate::net::net_controller::{NetController, NetService}; +use crate::net::utils::find_wifi_iface; use crate::net::web_server::{UpgradableListener, WebServerAcceptorSetter}; use crate::prelude::*; use crate::progress::{ @@ -197,7 +199,8 @@ pub async fn init_postgres(datadir: impl AsRef) -> Result<(), Error> { } pub struct InitResult { - pub net_ctrl: PreInitNetController, + pub net_ctrl: Arc, + pub os_net_service: NetService, } pub struct InitPhases { @@ -347,19 +350,21 @@ pub async fn init( let account = AccountInfo::load(&peek)?; start_net.start(); - let net_ctrl = PreInitNetController::init( - db.clone(), - cfg.tor_control - .unwrap_or(SocketAddr::from(([127, 0, 0, 1], 9051))), - cfg.tor_socks.unwrap_or(SocketAddr::V4(SocketAddrV4::new( - Ipv4Addr::new(127, 0, 0, 1), - 9050, - ))), - &account.hostname, - account.tor_key, - ) - .await?; + let net_ctrl = Arc::new( + NetController::init( + db.clone(), + cfg.tor_control + .unwrap_or(SocketAddr::from(([127, 0, 0, 1], 9051))), + cfg.tor_socks.unwrap_or(SocketAddr::V4(SocketAddrV4::new( + Ipv4Addr::new(127, 0, 0, 1), + 9050, + ))), + &account.hostname, + ) + .await?, + ); webserver.try_upgrade(|a| net_ctrl.net_iface.upgrade_listener(a))?; + let os_net_service = net_ctrl.os_bindings().await?; start_net.complete(); mount_logs.start(); @@ -394,8 +399,6 @@ pub async fn init( mount_logs.complete(); tracing::info!("Mounted Logs"); - let mut server_info = peek.as_public().as_server_info().de()?; - load_ca_cert.start(); // write to ca cert store tokio::fs::write( @@ -423,7 +426,15 @@ pub async fn init( load_ca_cert.complete(); load_wifi.start(); - crate::net::wifi::synchronize_network_manager(MAIN_DATA, &mut server_info.wifi).await?; + let wifi_interface = find_wifi_iface().await?; + let wifi = db + .mutate(|db| { + let wifi = db.as_public_mut().as_server_info_mut().as_wifi_mut(); + wifi.as_interface_mut().ser(&wifi_interface)?; + wifi.de() + }) + .await?; + crate::net::wifi::synchronize_network_manager(MAIN_DATA, &wifi).await?; load_wifi.complete(); tracing::info!("Synchronized WiFi"); @@ -448,8 +459,10 @@ pub async fn init( crate::disk::mount::util::bind(&tmp_docker, CONTAINER_DATADIR, false).await?; init_tmp.complete(); + let server_info = db.peek().await.into_public().into_server_info(); set_governor.start(); - let governor = if let Some(governor) = &server_info.governor { + let selected_governor = server_info.as_governor().de()?; + let governor = if let Some(governor) = &selected_governor { if cpupower::get_available_governors() .await? .contains(governor) @@ -470,11 +483,11 @@ pub async fn init( set_governor.complete(); sync_clock.start(); - server_info.ntp_synced = false; + let mut ntp_synced = false; let mut not_made_progress = 0u32; for _ in 0..1800 { if check_time_is_synchronized().await? { - server_info.ntp_synced = true; + ntp_synced = true; break; } let t = SystemTime::now(); @@ -491,7 +504,7 @@ pub async fn init( break; } } - if !server_info.ntp_synced { + if !ntp_synced { tracing::warn!("Timed out waiting for system time to synchronize"); } else { tracing::info!("Syncronized system clock"); @@ -499,15 +512,16 @@ pub async fn init( sync_clock.complete(); enable_zram.start(); - if server_info.zram { - crate::system::enable_zram().await? + if server_info.as_zram().de()? { + crate::system::enable_zram().await?; + tracing::info!("Enabled ZRAM"); } enable_zram.complete(); update_server_info.start(); - server_info.ram = get_mem_info().await?.total.0 as u64 * 1024 * 1024; - server_info.devices = lshw().await?; - server_info.status_info = ServerStatus { + let ram = get_mem_info().await?.total.0 as u64 * 1024 * 1024; + let devices = lshw().await?; + let status_info = ServerStatus { updated: false, update_progress: None, backup_progress: None, @@ -515,10 +529,15 @@ pub async fn init( restarting: false, }; db.mutate(|v| { - v.as_public_mut().as_server_info_mut().ser(&server_info)?; + let server_info = v.as_public_mut().as_server_info_mut(); + server_info.as_ntp_synced_mut().ser(&ntp_synced)?; + server_info.as_ram_mut().ser(&ram)?; + server_info.as_devices_mut().ser(&devices)?; + server_info.as_status_info_mut().ser(&status_info)?; Ok(()) }) .await?; + tracing::info!("Updated server info"); update_server_info.complete(); launch_service_network.start(); @@ -527,6 +546,7 @@ pub async fn init( .arg("lxc-net.service") .invoke(ErrorKind::Lxc) .await?; + tracing::info!("Launched service intranet"); launch_service_network.complete(); validate_db.start(); @@ -535,6 +555,7 @@ pub async fn init( d.ser(&model) }) .await?; + tracing::info!("Validated database"); validate_db.complete(); if let Some(progress) = postinit { @@ -543,7 +564,10 @@ pub async fn init( tracing::info!("System initialized."); - Ok(InitResult { net_ctrl }) + Ok(InitResult { + net_ctrl, + os_net_service, + }) } pub fn init_api() -> ParentHandler { diff --git a/core/startos/src/lib.rs b/core/startos/src/lib.rs index ad214da34..bcdcb7f91 100644 --- a/core/startos/src/lib.rs +++ b/core/startos/src/lib.rs @@ -87,6 +87,7 @@ use crate::context::{ CliContext, DiagnosticContext, InitContext, InstallContext, RpcContext, SetupContext, }; use crate::disk::fsck::RequiresReboot; +use crate::net::net; use crate::registry::context::{RegistryContext, RegistryUrlParams}; use crate::util::serde::HandlerExtSerde; @@ -313,7 +314,7 @@ pub fn server() -> ParentHandler { .no_display() .with_about("Remove system smtp server and credentials") .with_call_remote::() - ) + ).subcommand("host", net::host::server_host_api::().with_about("Commands for modifying the host for the system ui")) } pub fn package() -> ParentHandler { @@ -427,7 +428,7 @@ pub fn package() -> ParentHandler { .subcommand("attach", from_fn_async(service::cli_attach).no_display()) .subcommand( "host", - net::host::host::().with_about("Manage network hosts for a package"), + net::host::host_api::().with_about("Manage network hosts for a package"), ) } diff --git a/core/startos/src/middleware/cors.rs b/core/startos/src/middleware/cors.rs index a8c406a8a..60e8247ea 100644 --- a/core/startos/src/middleware/cors.rs +++ b/core/startos/src/middleware/cors.rs @@ -1,6 +1,7 @@ +use axum::body::Body; use axum::extract::Request; use axum::response::Response; -use http::{HeaderMap, HeaderValue}; +use http::{HeaderMap, HeaderValue, Method}; use rpc_toolkit::{Empty, Middleware}; #[derive(Clone)] @@ -52,6 +53,13 @@ impl Middleware for Cors { request: &mut Request, ) -> Result<(), Response> { self.get_cors_headers(request); + if request.method() == Method::OPTIONS { + let mut response = Response::new(Body::empty()); + response + .headers_mut() + .extend(std::mem::take(&mut self.headers)); + return Err(response); + } Ok(()) } async fn process_http_response(&mut self, _: &Context, response: &mut Response) { diff --git a/core/startos/src/net/acme.rs b/core/startos/src/net/acme.rs index 5d8da41f1..9ef2af1f7 100644 --- a/core/startos/src/net/acme.rs +++ b/core/startos/src/net/acme.rs @@ -173,16 +173,26 @@ impl<'a> async_acme::cache::AcmeCache for AcmeCertCache<'a> { } pub fn acme() -> ParentHandler { - ParentHandler::new().subcommand( - "init", - from_fn_async(init) - .no_display() - .with_about("Setup ACME certificate acquisition") - .with_call_remote::(), - ) + ParentHandler::new() + .subcommand( + "init", + from_fn_async(init) + .with_metadata("sync_db", Value::Bool(true)) + .no_display() + .with_about("Setup ACME certificate acquisition") + .with_call_remote::(), + ) + .subcommand( + "remove", + from_fn_async(remove) + .with_metadata("sync_db", Value::Bool(true)) + .no_display() + .with_about("Setup ACME certificate acquisition") + .with_call_remote::(), + ) } -#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize, TS)] +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, TS)] #[ts(type = "string")] pub struct AcmeProvider(pub Url); impl FromStr for AcmeProvider { @@ -193,9 +203,31 @@ impl FromStr for AcmeProvider { "letsencrypt-staging" => async_acme::acme::LETS_ENCRYPT_STAGING_DIRECTORY.parse(), s => s.parse(), } + .map(|mut u: Url| { + let path = u + .path_segments() + .into_iter() + .flatten() + .filter(|p| !p.is_empty()) + .map(|p| p.to_owned()) + .collect::>(); + if let Ok(mut path_mut) = u.path_segments_mut() { + path_mut.clear(); + path_mut.extend(path); + } + u + }) .map(Self) } } +impl<'de> Deserialize<'de> for AcmeProvider { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + crate::util::serde::deserialize_from_str(deserializer) + } +} impl AsRef for AcmeProvider { fn as_ref(&self) -> &str { self.0.as_str() @@ -230,3 +262,24 @@ pub async fn init( .await?; Ok(()) } + +#[derive(Deserialize, Serialize, Parser)] +pub struct RemoveAcmeParams { + #[arg(long)] + pub provider: AcmeProvider, +} + +pub async fn remove( + ctx: RpcContext, + RemoveAcmeParams { provider }: RemoveAcmeParams, +) -> Result<(), Error> { + ctx.db + .mutate(|db| { + db.as_public_mut() + .as_server_info_mut() + .as_acme_mut() + .remove(&provider) + }) + .await?; + Ok(()) +} diff --git a/core/startos/src/net/dns.rs b/core/startos/src/net/dns.rs index 016e5de9f..d68a971f1 100644 --- a/core/startos/src/net/dns.rs +++ b/core/startos/src/net/dns.rs @@ -1,6 +1,6 @@ use std::borrow::Borrow; use std::collections::BTreeMap; -use std::net::{Ipv4Addr, SocketAddr}; +use std::net::Ipv4Addr; use std::sync::{Arc, Weak}; use std::time::Duration; @@ -19,6 +19,7 @@ use trust_dns_server::server::{Request, RequestHandler, ResponseHandler, Respons use trust_dns_server::ServerFuture; use crate::net::forward::START9_BRIDGE_IFACE; +use crate::util::sync::Watch; use crate::util::Invoke; use crate::{Error, ErrorKind, ResultExt}; @@ -140,38 +141,46 @@ impl RequestHandler for Resolver { impl DnsController { #[instrument(skip_all)] - pub async fn init(bind: &[SocketAddr]) -> Result { + pub async fn init(mut lxcbr_status: Watch) -> Result { let services = Arc::new(RwLock::new(BTreeMap::new())); let mut server = ServerFuture::new(Resolver { services: services.clone(), }); - server.register_listener( - TcpListener::bind(bind) - .await - .with_kind(ErrorKind::Network)?, - Duration::from_secs(30), - ); - server.register_socket(UdpSocket::bind(bind).await.with_kind(ErrorKind::Network)?); - Command::new("resolvectl") - .arg("dns") - .arg(START9_BRIDGE_IFACE) - .arg("127.0.0.1") - .invoke(ErrorKind::Network) - .await?; - Command::new("resolvectl") - .arg("domain") - .arg(START9_BRIDGE_IFACE) - .arg("embassy") - .invoke(ErrorKind::Network) - .await?; + let dns_server = tokio::spawn(async move { + server.register_listener( + TcpListener::bind((Ipv4Addr::LOCALHOST, 53)) + .await + .with_kind(ErrorKind::Network)?, + Duration::from_secs(30), + ); + server.register_socket( + UdpSocket::bind((Ipv4Addr::LOCALHOST, 53)) + .await + .with_kind(ErrorKind::Network)?, + ); + + lxcbr_status.wait_for(|a| *a).await; + + Command::new("resolvectl") + .arg("dns") + .arg(START9_BRIDGE_IFACE) + .arg("127.0.0.1") + .invoke(ErrorKind::Network) + .await?; + Command::new("resolvectl") + .arg("domain") + .arg(START9_BRIDGE_IFACE) + .arg("embassy") + .invoke(ErrorKind::Network) + .await?; - let dns_server = tokio::spawn( server .block_until_done() - .map_err(|e| Error::new(e, ErrorKind::Network)), - ) + .await + .map_err(|e| Error::new(e, ErrorKind::Network)) + }) .into(); Ok(Self { diff --git a/core/startos/src/net/host/address.rs b/core/startos/src/net/host/address.rs index fa41824a5..b75734a13 100644 --- a/core/startos/src/net/host/address.rs +++ b/core/startos/src/net/host/address.rs @@ -1,13 +1,16 @@ +use std::collections::BTreeSet; + use clap::Parser; use imbl_value::InternedString; -use models::{HostId, PackageId}; use rpc_toolkit::{from_fn_async, Context, Empty, HandlerArgs, HandlerExt, ParentHandler}; use serde::{Deserialize, Serialize}; use torut::onion::OnionAddressV3; use ts_rs::TS; use crate::context::{CliContext, RpcContext}; +use crate::db::model::DatabaseModel; use crate::net::acme::AcmeProvider; +use crate::net::host::{all_hosts, HostApiKind}; use crate::prelude::*; use crate::util::serde::{display_serializable, HandlerExtSerde}; @@ -35,19 +38,51 @@ pub struct DomainConfig { pub acme: Option, } -#[derive(Deserialize, Serialize, Parser)] -pub struct AddressApiParams { - host: HostId, +fn check_duplicates(db: &DatabaseModel) -> Result<(), Error> { + let mut onions = BTreeSet::::new(); + let mut domains = BTreeSet::::new(); + let mut check_onion = |onion: OnionAddressV3| { + if onions.contains(&onion) { + return Err(Error::new( + eyre!("onion address {onion} is already in use"), + ErrorKind::InvalidRequest, + )); + } + onions.insert(onion); + Ok(()) + }; + let mut check_domain = |domain: InternedString| { + if domains.contains(&domain) { + return Err(Error::new( + eyre!("domain {domain} is already in use"), + ErrorKind::InvalidRequest, + )); + } + domains.insert(domain); + Ok(()) + }; + for host in all_hosts(db) { + let host = host?; + for onion in host.as_onions().de()? { + check_onion(onion)?; + } + for domain in host.as_domains().keys()? { + check_domain(domain)?; + } + } + Ok(()) } -pub fn address() -> ParentHandler { - ParentHandler::::new() +pub fn address_api( +) -> ParentHandler { + ParentHandler::::new() .subcommand( "domain", - ParentHandler::::new() + ParentHandler::::new() .subcommand( "add", - from_fn_async(add_domain) + from_fn_async(add_domain::) + .with_metadata("sync_db", Value::Bool(true)) .with_inherited(|_, a| a) .no_display() .with_about("Add an address to this host") @@ -55,20 +90,22 @@ pub fn address() -> ParentHandler { ) .subcommand( "remove", - from_fn_async(remove_domain) + from_fn_async(remove_domain::) + .with_metadata("sync_db", Value::Bool(true)) .with_inherited(|_, a| a) .no_display() .with_about("Remove an address from this host") .with_call_remote::(), ) - .with_inherited(|AddressApiParams { host }, package| (package, host)), + .with_inherited(Kind::inheritance), ) .subcommand( "onion", - ParentHandler::::new() + ParentHandler::::new() .subcommand( "add", - from_fn_async(add_onion) + from_fn_async(add_onion::) + .with_metadata("sync_db", Value::Bool(true)) .with_inherited(|_, a| a) .no_display() .with_about("Add an address to this host") @@ -76,18 +113,19 @@ pub fn address() -> ParentHandler { ) .subcommand( "remove", - from_fn_async(remove_onion) + from_fn_async(remove_onion::) + .with_metadata("sync_db", Value::Bool(true)) .with_inherited(|_, a| a) .no_display() .with_about("Remove an address from this host") .with_call_remote::(), ) - .with_inherited(|AddressApiParams { host }, package| (package, host)), + .with_inherited(Kind::inheritance), ) .subcommand( "list", - from_fn_async(list_addresses) - .with_inherited(|AddressApiParams { host }, package| (package, host)) + from_fn_async(list_addresses::) + .with_inherited(Kind::inheritance) .with_display_serializable() .with_custom_display_fn(|HandlerArgs { params, .. }, res| { use prettytable::*; @@ -136,14 +174,14 @@ pub struct AddDomainParams { pub acme: Option, } -pub async fn add_domain( +pub async fn add_domain( ctx: RpcContext, AddDomainParams { domain, private, acme, }: AddDomainParams, - (package, host): (PackageId, HostId), + inheritance: Kind::Inheritance, ) -> Result<(), Error> { ctx.db .mutate(|db| { @@ -153,13 +191,7 @@ pub async fn add_domain( } } - db.as_public_mut() - .as_package_data_mut() - .as_idx_mut(&package) - .or_not_found(&package)? - .as_hosts_mut() - .as_idx_mut(&host) - .or_not_found(&host)? + Kind::host_for(&inheritance, db)? .as_domains_mut() .insert( &domain, @@ -167,12 +199,11 @@ pub async fn add_domain( public: !private, acme, }, - ) + )?; + check_duplicates(db) }) .await?; - let service = ctx.services.get(&package).await; - let service_ref = service.as_ref().or_not_found(&package)?; - service_ref.update_host(host).await?; + Kind::sync_host(&ctx, inheritance).await?; Ok(()) } @@ -182,27 +213,19 @@ pub struct RemoveDomainParams { pub domain: InternedString, } -pub async fn remove_domain( +pub async fn remove_domain( ctx: RpcContext, RemoveDomainParams { domain }: RemoveDomainParams, - (package, host): (PackageId, HostId), + inheritance: Kind::Inheritance, ) -> Result<(), Error> { ctx.db .mutate(|db| { - db.as_public_mut() - .as_package_data_mut() - .as_idx_mut(&package) - .or_not_found(&package)? - .as_hosts_mut() - .as_idx_mut(&host) - .or_not_found(&host)? + Kind::host_for(&inheritance, db)? .as_domains_mut() .remove(&domain) }) .await?; - let service = ctx.services.get(&package).await; - let service_ref = service.as_ref().or_not_found(&package)?; - service_ref.update_host(host).await?; + Kind::sync_host(&ctx, inheritance).await?; Ok(()) } @@ -212,10 +235,10 @@ pub struct OnionParams { pub onion: String, } -pub async fn add_onion( +pub async fn add_onion( ctx: RpcContext, OnionParams { onion }: OnionParams, - (package, host): (PackageId, HostId), + inheritance: Kind::Inheritance, ) -> Result<(), Error> { let onion = onion .strip_suffix(".onion") @@ -230,28 +253,22 @@ pub async fn add_onion( .mutate(|db| { db.as_private().as_key_store().as_onion().get_key(&onion)?; - db.as_public_mut() - .as_package_data_mut() - .as_idx_mut(&package) - .or_not_found(&package)? - .as_hosts_mut() - .as_idx_mut(&host) - .or_not_found(&host)? + Kind::host_for(&inheritance, db)? .as_onions_mut() - .mutate(|a| Ok(a.insert(onion))) + .mutate(|a| Ok(a.insert(onion)))?; + check_duplicates(db) }) .await?; - let service = ctx.services.get(&package).await; - let service_ref = service.as_ref().or_not_found(&package)?; - service_ref.update_host(host).await?; + + Kind::sync_host(&ctx, inheritance).await?; Ok(()) } -pub async fn remove_onion( +pub async fn remove_onion( ctx: RpcContext, OnionParams { onion }: OnionParams, - (package, host): (PackageId, HostId), + inheritance: Kind::Inheritance, ) -> Result<(), Error> { let onion = onion .strip_suffix(".onion") @@ -264,40 +281,23 @@ pub async fn remove_onion( .parse::()?; ctx.db .mutate(|db| { - db.as_public_mut() - .as_package_data_mut() - .as_idx_mut(&package) - .or_not_found(&package)? - .as_hosts_mut() - .as_idx_mut(&host) - .or_not_found(&host)? + Kind::host_for(&inheritance, db)? .as_onions_mut() .mutate(|a| Ok(a.remove(&onion))) }) .await?; - let service = ctx.services.get(&package).await; - let service_ref = service.as_ref().or_not_found(&package)?; - service_ref.update_host(host).await?; + + Kind::sync_host(&ctx, inheritance).await?; Ok(()) } -pub async fn list_addresses( +pub async fn list_addresses( ctx: RpcContext, _: Empty, - (package, host): (PackageId, HostId), + inheritance: Kind::Inheritance, ) -> Result, Error> { - Ok(ctx - .db - .peek() - .await - .into_public() - .into_package_data() - .into_idx(&package) - .or_not_found(&package)? - .into_hosts() - .into_idx(&host) - .or_not_found(&host)? + Ok(Kind::host_for(&inheritance, &mut ctx.db.peek().await)? .de()? .addresses() .collect()) diff --git a/core/startos/src/net/host/binding.rs b/core/startos/src/net/host/binding.rs index 08f9f68c4..5b726a82d 100644 --- a/core/startos/src/net/host/binding.rs +++ b/core/startos/src/net/host/binding.rs @@ -3,13 +3,14 @@ use std::str::FromStr; use clap::builder::ValueParserFactory; use clap::Parser; -use models::{FromStrParser, HostId, PackageId}; +use models::{FromStrParser, HostId}; use rpc_toolkit::{from_fn_async, Context, Empty, HandlerArgs, HandlerExt, ParentHandler}; use serde::{Deserialize, Serialize}; use ts_rs::TS; use crate::context::{CliContext, RpcContext}; use crate::net::forward::AvailablePorts; +use crate::net::host::HostApiKind; use crate::net::vhost::AlpnInfo; use crate::prelude::*; use crate::util::serde::{display_serializable, HandlerExtSerde}; @@ -146,17 +147,13 @@ pub struct AddSslOptions { pub alpn: Option, } -#[derive(Deserialize, Serialize, Parser)] -pub struct BindingApiParams { - host: HostId, -} - -pub fn binding() -> ParentHandler { - ParentHandler::::new() +pub fn binding( +) -> ParentHandler { + ParentHandler::::new() .subcommand( "list", - from_fn_async(list_bindings) - .with_inherited(|BindingApiParams { host }, package| (package, host)) + from_fn_async(list_bindings::) + .with_inherited(Kind::inheritance) .with_display_serializable() .with_custom_display_fn(|HandlerArgs { params, .. }, res| { use prettytable::*; @@ -194,30 +191,22 @@ pub fn binding() -> ParentHandler { ) .subcommand( "set-public", - from_fn_async(set_public) - .with_inherited(|BindingApiParams { host }, package| (package, host)) + from_fn_async(set_public::) + .with_metadata("sync_db", Value::Bool(true)) + .with_inherited(Kind::inheritance) .no_display() .with_about("Add an binding to this host") .with_call_remote::(), ) } -pub async fn list_bindings( +pub async fn list_bindings( ctx: RpcContext, _: Empty, - (package, host): (PackageId, HostId), + inheritance: Kind::Inheritance, ) -> Result, Error> { - ctx.db - .peek() - .await - .into_public() - .into_package_data() - .into_idx(&package) - .or_not_found(&package)? - .into_hosts() - .into_idx(&host) - .or_not_found(&host)? - .into_bindings() + Kind::host_for(&inheritance, &mut ctx.db.peek().await)? + .as_bindings() .de() } @@ -230,23 +219,17 @@ pub struct BindingSetPublicParams { public: Option, } -pub async fn set_public( +pub async fn set_public( ctx: RpcContext, BindingSetPublicParams { internal_port, public, }: BindingSetPublicParams, - (package, host): (PackageId, HostId), + inheritance: Kind::Inheritance, ) -> Result<(), Error> { ctx.db .mutate(|db| { - db.as_public_mut() - .as_package_data_mut() - .as_idx_mut(&package) - .or_not_found(&package)? - .as_hosts_mut() - .as_idx_mut(&host) - .or_not_found(&host)? + Kind::host_for(&inheritance, db)? .as_bindings_mut() .mutate(|b| { b.get_mut(&internal_port) @@ -257,11 +240,5 @@ pub async fn set_public( }) }) .await?; - ctx.services - .get(&package) - .await - .as_ref() - .or_not_found(&package)? - .update_host(host) - .await + Kind::sync_host(&ctx, inheritance).await } diff --git a/core/startos/src/net/host/mod.rs b/core/startos/src/net/host/mod.rs index 9f4194866..df209cef5 100644 --- a/core/startos/src/net/host/mod.rs +++ b/core/startos/src/net/host/mod.rs @@ -1,9 +1,12 @@ use std::collections::{BTreeMap, BTreeSet}; +use std::future::Future; +use std::panic::RefUnwindSafe; use clap::Parser; use imbl_value::InternedString; +use itertools::Itertools; use models::{HostId, PackageId}; -use rpc_toolkit::{from_fn_async, Context, Empty, HandlerExt, ParentHandler}; +use rpc_toolkit::{from_fn_async, Context, Empty, HandlerExt, OrEmpty, ParentHandler}; use serde::{Deserialize, Serialize}; use torut::onion::OnionAddressV3; use ts_rs::TS; @@ -11,7 +14,7 @@ use ts_rs::TS; use crate::context::RpcContext; use crate::db::model::DatabaseModel; use crate::net::forward::AvailablePorts; -use crate::net::host::address::{address, DomainConfig, HostAddress}; +use crate::net::host::address::{address_api, DomainConfig, HostAddress}; use crate::net::host::binding::{binding, BindInfo, BindOptions}; use crate::net::service_interface::HostnameInfo; use crate::prelude::*; @@ -19,12 +22,11 @@ use crate::prelude::*; pub mod address; pub mod binding; -#[derive(Debug, Deserialize, Serialize, HasModel, TS)] +#[derive(Debug, Default, Deserialize, Serialize, HasModel, TS)] #[serde(rename_all = "camelCase")] #[model = "Model"] #[ts(export)] pub struct Host { - pub kind: HostKind, pub bindings: BTreeMap, #[ts(type = "string[]")] pub onions: BTreeSet, @@ -39,14 +41,8 @@ impl AsRef for Host { } } impl Host { - pub fn new(kind: HostKind) -> Self { - Self { - kind, - bindings: BTreeMap::new(), - onions: BTreeSet::new(), - domains: BTreeMap::new(), - hostname_info: BTreeMap::new(), - } + pub fn new() -> Self { + Self::default() } pub fn addresses<'a>(&'a self) -> impl Iterator + 'a { self.onions @@ -67,15 +63,6 @@ impl Host { } } -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize, TS)] -#[serde(rename_all = "camelCase")] -#[ts(export)] -pub enum HostKind { - Multi, - // Single, - // Static, -} - #[derive(Debug, Default, Deserialize, Serialize, HasModel, TS)] #[model = "Model"] #[ts(export)] @@ -94,10 +81,12 @@ impl Map for Hosts { pub fn host_for<'a>( db: &'a mut DatabaseModel, - package_id: &PackageId, + package_id: Option<&PackageId>, host_id: &HostId, - host_kind: HostKind, ) -> Result<&'a mut Model, Error> { + let Some(package_id) = package_id else { + return Ok(db.as_public_mut().as_server_info_mut().as_host_mut()); + }; fn host_info<'a>( db: &'a mut DatabaseModel, package_id: &PackageId, @@ -121,7 +110,7 @@ pub fn host_for<'a>( None }; host_info(db, package_id)?.upsert(host_id, || { - let mut h = Host::new(host_kind); + let mut h = Host::new(); h.onions.insert( tor_key .or_not_found("generated tor key")? @@ -132,12 +121,20 @@ pub fn host_for<'a>( }) } +pub fn all_hosts(db: &DatabaseModel) -> impl Iterator, Error>> { + [Ok(db.as_public().as_server_info().as_host())] + .into_iter() + .chain( + [db.as_public().as_package_data().as_entries()] + .into_iter() + .flatten_ok() + .map(|entry| entry.and_then(|(_, v)| v.as_hosts().as_entries())) + .flatten_ok() + .map_ok(|(_, v)| v), + ) +} + impl Model { - pub fn set_kind(&mut self, kind: HostKind) -> Result<(), Error> { - match (self.as_kind().de()?, kind) { - (HostKind::Multi, HostKind::Multi) => Ok(()), - } - } pub fn add_binding( &mut self, available_ports: &mut AvailablePorts, @@ -157,16 +154,78 @@ impl Model { } #[derive(Deserialize, Serialize, Parser)] -pub struct HostParams { +pub struct RequiresPackageId { package: PackageId, } -pub fn host() -> ParentHandler { - ParentHandler::::new() +#[derive(Deserialize, Serialize, Parser)] +pub struct RequiresHostId { + host: HostId, +} + +pub trait HostApiKind: 'static { + type Params: Send + Sync + 'static; + type InheritedParams: Send + Sync + 'static; + type Inheritance: RefUnwindSafe + OrEmpty + Send + Sync + 'static; + fn inheritance(params: Self::Params, inherited: Self::InheritedParams) -> Self::Inheritance; + fn host_for<'a>( + inheritance: &Self::Inheritance, + db: &'a mut DatabaseModel, + ) -> Result<&'a mut Model, Error>; + fn sync_host( + ctx: &RpcContext, + inheritance: Self::Inheritance, + ) -> impl Future> + Send; +} +pub struct ForPackage; +impl HostApiKind for ForPackage { + type Params = RequiresHostId; + type InheritedParams = PackageId; + type Inheritance = (PackageId, HostId); + fn inheritance( + RequiresHostId { host }: Self::Params, + package: Self::InheritedParams, + ) -> Self::Inheritance { + (package, host) + } + fn host_for<'a>( + (package, host): &Self::Inheritance, + db: &'a mut DatabaseModel, + ) -> Result<&'a mut Model, Error> { + host_for(db, Some(package), host) + } + async fn sync_host(ctx: &RpcContext, (package, host): Self::Inheritance) -> Result<(), Error> { + let service = ctx.services.get(&package).await; + let service_ref = service.as_ref().or_not_found(&package)?; + service_ref.sync_host(host).await?; + Ok(()) + } +} +pub struct ForServer; +impl HostApiKind for ForServer { + type Params = Empty; + type InheritedParams = Empty; + type Inheritance = Empty; + fn inheritance(_: Self::Params, _: Self::InheritedParams) -> Self::Inheritance { + Empty {} + } + fn host_for<'a>( + _: &Self::Inheritance, + db: &'a mut DatabaseModel, + ) -> Result<&'a mut Model, Error> { + host_for(db, None, &HostId::default()) + } + async fn sync_host(ctx: &RpcContext, _: Self::Inheritance) -> Result<(), Error> { + ctx.os_net_service.sync_host(HostId::default()).await + } +} + +pub fn host_api() -> ParentHandler { + ParentHandler::::new() .subcommand( "list", from_fn_async(list_hosts) - .with_inherited(|HostParams { package }, _| package) + .with_inherited(|RequiresPackageId { package }, _| package) .with_custom_display_fn(|_, ids| { for id in ids { println!("{id}") @@ -177,14 +236,21 @@ pub fn host() -> ParentHandler { ) .subcommand( "address", - address::().with_inherited(|HostParams { package }, _| package), + address_api::() + .with_inherited(|RequiresPackageId { package }, _| package), ) .subcommand( "binding", - binding::().with_inherited(|HostParams { package }, _| package), + binding::().with_inherited(|RequiresPackageId { package }, _| package), ) } +pub fn server_host_api() -> ParentHandler { + ParentHandler::::new() + .subcommand("address", address_api::()) + .subcommand("binding", binding::()) +} + pub async fn list_hosts( ctx: RpcContext, _: Empty, diff --git a/core/startos/src/net/keys.rs b/core/startos/src/net/keys.rs index 866b2ca06..2cfcb025d 100644 --- a/core/startos/src/net/keys.rs +++ b/core/startos/src/net/keys.rs @@ -21,7 +21,9 @@ impl KeyStore { local_certs: CertStore::new(account)?, acme: AcmeCertStore::new(), }; - res.onion.insert(account.tor_key.clone()); + for tor_key in account.tor_keys.iter().cloned() { + res.onion.insert(tor_key); + } Ok(res) } } diff --git a/core/startos/src/net/net_controller.rs b/core/startos/src/net/net_controller.rs index 7ff707fbe..8c3fe6ae1 100644 --- a/core/startos/src/net/net_controller.rs +++ b/core/startos/src/net/net_controller.rs @@ -7,6 +7,8 @@ use imbl::OrdMap; use imbl_value::InternedString; use ipnet::IpNet; use models::{HostId, OptionExt, PackageId}; +use tokio::sync::Mutex; +use tokio::task::JoinHandle; use torut::onion::{OnionAddressV3, TorSecretKeyV3}; use tracing::instrument; @@ -16,8 +18,8 @@ use crate::hostname::Hostname; use crate::net::dns::DnsController; use crate::net::forward::LanPortForwardController; use crate::net::host::address::HostAddress; -use crate::net::host::binding::{BindId, BindOptions}; -use crate::net::host::{host_for, Host, HostKind, Hosts}; +use crate::net::host::binding::{AddSslOptions, BindId, BindOptions}; +use crate::net::host::{host_for, Host, Hosts}; use crate::net::network_interface::NetworkInterfaceController; use crate::net::service_interface::{HostnameInfo, IpHostname, OnionHostname}; use crate::net::tor::TorController; @@ -27,129 +29,44 @@ use crate::prelude::*; use crate::util::serde::MaybeUtf8String; use crate::HOST_IP; -pub struct PreInitNetController { - pub db: TypedPatchDb, - tor: TorController, - vhost: VHostController, - pub net_iface: Arc, - os_bindings: Vec>, - server_hostnames: Vec>, -} -impl PreInitNetController { - #[instrument(skip_all)] - pub async fn init( - db: TypedPatchDb, - tor_control: SocketAddr, - tor_socks: SocketAddr, - hostname: &Hostname, - os_tor_key: TorSecretKeyV3, - ) -> Result { - let net_iface = Arc::new(NetworkInterfaceController::new(db.clone())); - let mut res = Self { - db: db.clone(), - tor: TorController::new(tor_control, tor_socks), - vhost: VHostController::new(db, net_iface.clone()), - net_iface, - os_bindings: Vec::new(), - server_hostnames: Vec::new(), - }; - res.add_os_bindings(hostname, os_tor_key).await?; - Ok(res) - } - - async fn add_os_bindings( - &mut self, - hostname: &Hostname, - tor_key: TorSecretKeyV3, - ) -> Result<(), Error> { - self.server_hostnames = vec![ - // LAN IP - None, - // Internal DNS - Some("embassy".into()), - Some("startos".into()), - // localhost - Some("localhost".into()), - Some(hostname.no_dot_host_name()), - // LAN mDNS - Some(hostname.local_domain_name()), - ]; - - let vhost_target = TargetInfo { - public: false, - acme: None, - addr: ([127, 0, 0, 1], 80).into(), - connect_ssl: Err(AlpnInfo::Specified(vec![ - MaybeUtf8String("http/1.1".into()), - MaybeUtf8String("h2".into()), - ])), - }; - - for hostname in self.server_hostnames.iter().cloned() { - self.os_bindings - .push(self.vhost.add(hostname, 443, vhost_target.clone())?); - } - - // Tor - self.os_bindings.push(self.vhost.add( - Some(InternedString::from_display( - &tor_key.public().get_onion_address(), - )), - 443, - vhost_target, - )?); - self.os_bindings.extend( - self.tor - .add( - tor_key, - vec![ - (80, ([127, 0, 0, 1], 80).into()), // http - (443, ([127, 0, 0, 1], 443).into()), // https - ], - ) - .await?, - ); - - Ok(()) - } -} - pub struct NetController { - db: TypedPatchDb, + pub(crate) db: TypedPatchDb, pub(super) tor: TorController, pub(super) vhost: VHostController, - pub net_iface: Arc, + pub(crate) net_iface: Arc, pub(super) dns: DnsController, pub(super) forward: LanPortForwardController, - pub(super) os_bindings: Vec>, pub(super) server_hostnames: Vec>, } impl NetController { pub async fn init( - PreInitNetController { - db, - tor, - vhost, - net_iface, - os_bindings, - server_hostnames, - }: PreInitNetController, - dns_bind: &[SocketAddr], + db: TypedPatchDb, + tor_control: SocketAddr, + tor_socks: SocketAddr, + hostname: &Hostname, ) -> Result { - let mut res = Self { - db, - tor, - vhost, - dns: DnsController::init(dns_bind).await?, + let net_iface = Arc::new(NetworkInterfaceController::new(db.clone())); + Ok(Self { + db: db.clone(), + tor: TorController::new(tor_control, tor_socks), + vhost: VHostController::new(db, net_iface.clone()), + dns: DnsController::init(net_iface.lxcbr_status()).await?, forward: LanPortForwardController::new(net_iface.subscribe()), net_iface, - os_bindings, - server_hostnames, - }; - res.os_bindings - .push(res.dns.add(None, HOST_IP.into()).await?); - Ok(res) + server_hostnames: vec![ + // LAN IP + None, + // Internal DNS + Some("embassy".into()), + Some("startos".into()), + // localhost + Some("localhost".into()), + Some(hostname.no_dot_host_name()), + // LAN mDNS + Some(hostname.local_domain_name()), + ], + }) } #[instrument(skip_all)] @@ -160,17 +77,48 @@ impl NetController { ) -> Result { let dns = self.dns.add(Some(package.clone()), ip).await?; - let mut res = NetService { - shutdown: false, - id: package, + let res = NetService::new(NetServiceData { + id: Some(package), ip, dns, controller: Arc::downgrade(self), binds: BTreeMap::new(), - }; + })?; res.clear_bindings(Default::default()).await?; Ok(res) } + + pub async fn os_bindings(self: &Arc) -> Result { + let dns = self.dns.add(None, HOST_IP.into()).await?; + + let service = NetService::new(NetServiceData { + id: None, + ip: [127, 0, 0, 1].into(), + dns, + controller: Arc::downgrade(self), + binds: BTreeMap::new(), + })?; + service.clear_bindings(Default::default()).await?; + service + .bind( + HostId::default(), + 80, + BindOptions { + preferred_external_port: 80, + add_ssl: Some(AddSslOptions { + preferred_external_port: 443, + alpn: Some(AlpnInfo::Specified(vec![ + MaybeUtf8String("http/1.1".into()), + MaybeUtf8String("h2".into()), + ])), + }), + secure: None, + }, + ) + .await?; + + Ok(service) + } } #[derive(Default, Debug)] @@ -180,15 +128,14 @@ struct HostBinds { tor: BTreeMap, Vec>)>, } -pub struct NetService { - shutdown: bool, - id: PackageId, +pub struct NetServiceData { + id: Option, ip: Ipv4Addr, dns: Arc<()>, controller: Weak, binds: BTreeMap, } -impl NetService { +impl NetServiceData { fn net_controller(&self) -> Result, Error> { Weak::upgrade(&self.controller).ok_or_else(|| { Error::new( @@ -198,49 +145,54 @@ impl NetService { }) } - pub async fn bind( + async fn clear_bindings( &mut self, - kind: HostKind, - id: HostId, - internal_port: u16, - options: BindOptions, + ctrl: &NetController, + except: BTreeSet, ) -> Result<(), Error> { - crate::dbg!("bind", &kind, &id, internal_port, &options); - let pkg_id = &self.id; - let host = self - .net_controller()? - .db - .mutate(|db| { - let mut ports = db.as_private().as_available_ports().de()?; - let host = host_for(db, pkg_id, &id, kind)?; - host.add_binding(&mut ports, internal_port, options)?; - let host = host.de()?; - db.as_private_mut().as_available_ports_mut().ser(&ports)?; - Ok(host) - }) - .await?; - self.update(id, host).await - } - - pub async fn clear_bindings(&mut self, except: BTreeSet) -> Result<(), Error> { - let pkg_id = &self.id; - let hosts = self - .net_controller()? - .db - .mutate(|db| { - let mut res = Hosts::default(); - for (host_id, host) in db - .as_public_mut() - .as_package_data_mut() - .as_idx_mut(pkg_id) - .or_not_found(pkg_id)? - .as_hosts_mut() - .as_entries_mut()? - { + if let Some(pkg_id) = &self.id { + let hosts = ctrl + .db + .mutate(|db| { + let mut res = Hosts::default(); + for (host_id, host) in db + .as_public_mut() + .as_package_data_mut() + .as_idx_mut(pkg_id) + .or_not_found(pkg_id)? + .as_hosts_mut() + .as_entries_mut()? + { + host.as_bindings_mut().mutate(|b| { + for (internal_port, info) in b { + if !except.contains(&BindId { + id: host_id.clone(), + internal_port: *internal_port, + }) { + info.disable(); + } + } + Ok(()) + })?; + res.0.insert(host_id, host.de()?); + } + Ok(res) + }) + .await?; + let mut errors = ErrorCollection::new(); + for (id, host) in hosts.0 { + errors.handle(self.update(ctrl, id, host).await); + } + errors.into_result() + } else { + let host = ctrl + .db + .mutate(|db| { + let host = db.as_public_mut().as_server_info_mut().as_host_mut(); host.as_bindings_mut().mutate(|b| { for (internal_port, info) in b { if !except.contains(&BindId { - id: host_id.clone(), + id: HostId::default(), internal_port: *internal_port, }) { info.disable(); @@ -248,20 +200,14 @@ impl NetService { } Ok(()) })?; - res.0.insert(host_id, host.de()?); - } - Ok(res) - }) - .await?; - let mut errors = ErrorCollection::new(); - for (id, host) in hosts.0 { - errors.handle(self.update(id, host).await); + host.de() + }) + .await?; + self.update(ctrl, HostId::default(), host).await } - errors.into_result() } - pub async fn update(&mut self, id: HostId, host: Host) -> Result<(), Error> { - let ctrl = self.net_controller()?; + async fn update(&mut self, ctrl: &NetController, id: HostId, host: Host) -> Result<(), Error> { let mut forwards: BTreeMap = BTreeMap::new(); let mut vhosts: BTreeMap<(Option, u16), TargetInfo> = BTreeMap::new(); let mut tor: BTreeMap)> = @@ -630,7 +576,7 @@ impl NetService { ctrl.db .mutate(|db| { - host_for(db, &self.id, &id, host.kind)? + host_for(db, self.id.as_ref(), &id)? .as_hostname_info_mut() .ser(&hostname_info) }) @@ -638,10 +584,129 @@ impl NetService { Ok(()) } + async fn update_all(&mut self) -> Result<(), Error> { + let ctrl = self.net_controller()?; + if let Some(id) = &self.id { + for (host_id, host) in ctrl + .db + .peek() + .await + .as_public() + .as_package_data() + .as_idx(id) + .or_not_found(id)? + .as_hosts() + .as_entries()? + { + self.update(&*ctrl, host_id, host.de()?).await?; + } + } else { + self.update( + &*ctrl, + HostId::default(), + ctrl.db + .peek() + .await + .as_public() + .as_server_info() + .as_host() + .de()?, + ) + .await?; + } + Ok(()) + } +} + +pub struct NetService { + shutdown: bool, + data: Arc>, + sync_task: JoinHandle<()>, +} +impl NetService { + fn dummy() -> Self { + Self { + shutdown: true, + data: Arc::new(Mutex::new(NetServiceData { + id: None, + ip: Ipv4Addr::new(0, 0, 0, 0), + dns: Default::default(), + controller: Default::default(), + binds: BTreeMap::new(), + })), + sync_task: tokio::spawn(futures::future::ready(())), + } + } + + fn new(data: NetServiceData) -> Result { + let mut ip_info = data.net_controller()?.net_iface.subscribe(); + let data = Arc::new(Mutex::new(data)); + let thread_data = data.clone(); + let sync_task = tokio::spawn(async move { + loop { + if let Err(e) = thread_data.lock().await.update_all().await { + tracing::error!("Failed to update network info: {e}"); + tracing::debug!("{e:?}"); + } + ip_info.changed().await; + } + }); + Ok(Self { + shutdown: false, + data, + sync_task, + }) + } + + pub async fn bind( + &self, + id: HostId, + internal_port: u16, + options: BindOptions, + ) -> Result<(), Error> { + let mut data = self.data.lock().await; + let pkg_id = &data.id; + let ctrl = data.net_controller()?; + let host = ctrl + .db + .mutate(|db| { + let mut ports = db.as_private().as_available_ports().de()?; + let host = host_for(db, pkg_id.as_ref(), &id)?; + host.add_binding(&mut ports, internal_port, options)?; + let host = host.de()?; + db.as_private_mut().as_available_ports_mut().ser(&ports)?; + Ok(host) + }) + .await?; + data.update(&*ctrl, id, host).await + } + + pub async fn clear_bindings(&self, except: BTreeSet) -> Result<(), Error> { + let mut data = self.data.lock().await; + let ctrl = data.net_controller()?; + data.clear_bindings(&*ctrl, except).await + } + + pub async fn update(&self, id: HostId, host: Host) -> Result<(), Error> { + let mut data = self.data.lock().await; + let ctrl = data.net_controller()?; + data.update(&*ctrl, id, host).await + } + + pub async fn sync_host(&self, id: HostId) -> Result<(), Error> { + let mut data = self.data.lock().await; + let ctrl = data.net_controller()?; + let host = host_for(&mut ctrl.db.peek().await, data.id.as_ref(), &id)?.de()?; + data.update(&*ctrl, id, host).await + } + pub async fn remove_all(mut self) -> Result<(), Error> { - self.shutdown = true; - if let Some(ctrl) = Weak::upgrade(&self.controller) { - self.clear_bindings(Default::default()).await?; + self.sync_task.abort(); + let mut data = self.data.lock().await; + if let Some(ctrl) = Weak::upgrade(&data.controller) { + self.shutdown = true; + data.clear_bindings(&*ctrl, Default::default()).await?; + drop(ctrl); Ok(()) } else { @@ -653,26 +718,15 @@ impl NetService { } } - pub fn get_ip(&self) -> Ipv4Addr { - self.ip + pub async fn get_ip(&self) -> Ipv4Addr { + self.data.lock().await.ip } } impl Drop for NetService { fn drop(&mut self) { if !self.shutdown { - tracing::debug!("Dropping NetService for {}", self.id); - let svc = std::mem::replace( - self, - NetService { - shutdown: true, - id: Default::default(), - ip: Ipv4Addr::new(0, 0, 0, 0), - dns: Default::default(), - controller: Default::default(), - binds: BTreeMap::new(), - }, - ); + let svc = std::mem::replace(self, Self::dummy()); tokio::spawn(async move { svc.remove_all().await.log_err() }); } } diff --git a/core/startos/src/net/network_interface.rs b/core/startos/src/net/network_interface.rs index 920bdd4e4..bda34fc40 100644 --- a/core/startos/src/net/network_interface.rs +++ b/core/startos/src/net/network_interface.rs @@ -27,6 +27,7 @@ use zbus::{proxy, Connection}; use crate::context::{CliContext, RpcContext}; use crate::db::model::public::{IpInfo, NetworkInterfaceInfo, NetworkInterfaceType}; use crate::db::model::Database; +use crate::net::forward::START9_BRIDGE_IFACE; use crate::net::utils::{ipv6_is_link_local, ipv6_is_local}; use crate::prelude::*; use crate::util::future::Until; @@ -319,7 +320,10 @@ impl<'a> StubStream<'a> for SignalStream<'a> { } #[instrument(skip_all)] -async fn watcher(write_to: Watch>) { +async fn watcher( + write_to: Watch>, + lxcbr_status: Watch, +) { loop { let res: Result<(), Error> = async { let connection = Connection::system().await?; @@ -357,19 +361,27 @@ async fn watcher(write_to: Watch> let mut ifaces = BTreeSet::new(); let mut jobs = Vec::new(); for device in devices { + use futures::future::Either; + let device_proxy = device::DeviceProxy::new(&connection, device.clone()).await?; let iface = InternedString::intern(device_proxy.ip_interface().await?); if iface.is_empty() { continue; + } else if &*iface == START9_BRIDGE_IFACE { + jobs.push(Either::Left(watch_activated( + &connection, + device_proxy.clone(), + &lxcbr_status, + ))); } - jobs.push(watch_ip( + jobs.push(Either::Right(watch_ip( &connection, device_proxy.clone(), iface.clone(), &write_to, - )); + ))); ifaces.insert(iface); } @@ -588,13 +600,49 @@ async fn watch_ip( } } +#[instrument(skip(_connection, device_proxy, write_to))] +async fn watch_activated( + _connection: &Connection, + device_proxy: device::DeviceProxy<'_>, + write_to: &Watch, +) -> Result<(), Error> { + let mut until = Until::new() + .with_stream( + device_proxy + .receive_active_connection_changed() + .await + .stub(), + ) + .with_stream( + device_proxy + .receive_state_changed() + .await? + .into_inner() + .stub(), + ); + + loop { + until + .run(async { + write_to.send(device_proxy._state().await? == 100); + Ok(()) + }) + .await?; + } +} + pub struct NetworkInterfaceController { db: TypedPatchDb, + lxcbr_status: Watch, ip_info: Watch>, _watcher: NonDetachingJoinHandle<()>, listeners: SyncMutex>>, } impl NetworkInterfaceController { + pub fn lxcbr_status(&self) -> Watch { + self.lxcbr_status.clone_unseen() + } + pub fn subscribe(&self) -> Watch> { self.ip_info.clone_unseen() } @@ -665,8 +713,10 @@ impl NetworkInterfaceController { } pub fn new(db: TypedPatchDb) -> Self { let mut ip_info = Watch::new(BTreeMap::new()); + let lxcbr_status = Watch::new(false); Self { db: db.clone(), + lxcbr_status: lxcbr_status.clone(), ip_info: ip_info.clone(), _watcher: tokio::spawn(async move { match db @@ -688,7 +738,7 @@ impl NetworkInterfaceController { tracing::debug!("{e:?}"); } }; - tokio::join!(watcher(ip_info.clone()), async { + tokio::join!(watcher(ip_info.clone(), lxcbr_status), async { let res: Result<(), Error> = async { loop { if let Err(e) = async { diff --git a/core/startos/src/net/vhost.rs b/core/startos/src/net/vhost.rs index dc6f422bf..cbc1cc916 100644 --- a/core/startos/src/net/vhost.rs +++ b/core/startos/src/net/vhost.rs @@ -1,6 +1,5 @@ use std::collections::{BTreeMap, BTreeSet}; use std::net::{IpAddr, SocketAddr}; -use std::str::FromStr; use std::sync::{Arc, Weak}; use std::time::Duration; @@ -328,17 +327,23 @@ impl VHostServer { .headers() .get(http::header::HOST) .and_then(|host| host.to_str().ok()); - let uri = Uri::from_parts({ - let mut parts = req.uri().to_owned().into_parts(); - parts.scheme = Some("https".parse()?); - parts.authority = - host.map(FromStr::from_str).transpose()?; - parts - })?; - Response::builder() - .status(http::StatusCode::TEMPORARY_REDIRECT) - .header(http::header::LOCATION, uri.to_string()) - .body(Body::default()) + if let Some(host) = host { + let uri = Uri::from_parts({ + let mut parts = + req.uri().to_owned().into_parts(); + parts.scheme = Some("https".parse()?); + parts.authority = Some(host.parse()?); + parts + })?; + Response::builder() + .status(http::StatusCode::TEMPORARY_REDIRECT) + .header(http::header::LOCATION, uri.to_string()) + .body(Body::default()) + } else { + Response::builder() + .status(http::StatusCode::BAD_REQUEST) + .body(Body::from("Host header required")) + } } .await { diff --git a/core/startos/src/net/wifi.rs b/core/startos/src/net/wifi.rs index b2e59c20f..9b858aafe 100644 --- a/core/startos/src/net/wifi.rs +++ b/core/startos/src/net/wifi.rs @@ -899,9 +899,8 @@ impl TypedValueParser for CountryCodeParser { #[instrument(skip_all)] pub async fn synchronize_network_manager>( main_datadir: P, - wifi: &mut WifiInfo, + wifi: &WifiInfo, ) -> Result<(), Error> { - wifi.interface = find_wifi_iface().await?; let persistent = main_datadir.as_ref().join("system-connections"); if tokio::fs::metadata(&persistent).await.is_err() { diff --git a/core/startos/src/service/effects/net/bind.rs b/core/startos/src/service/effects/net/bind.rs index 2f3edb07b..2db3eb2ca 100644 --- a/core/startos/src/service/effects/net/bind.rs +++ b/core/startos/src/service/effects/net/bind.rs @@ -1,14 +1,12 @@ use models::{HostId, PackageId}; use crate::net::host::binding::{BindId, BindOptions, NetInfo}; -use crate::net::host::HostKind; use crate::service::effects::prelude::*; #[derive(Debug, Clone, Serialize, Deserialize, TS)] #[serde(rename_all = "camelCase")] #[ts(export)] pub struct BindParams { - kind: HostKind, id: HostId, internal_port: u16, #[serde(flatten)] @@ -17,15 +15,18 @@ pub struct BindParams { pub async fn bind( context: EffectContext, BindParams { - kind, id, internal_port, options, }: BindParams, ) -> Result<(), Error> { let context = context.deref()?; - let mut svc = context.seed.persistent_container.net_service.lock().await; - svc.bind(kind, id, internal_port, options).await + context + .seed + .persistent_container + .net_service + .bind(id, internal_port, options) + .await } #[derive(Debug, Clone, Serialize, Deserialize, TS, Parser)] @@ -41,8 +42,12 @@ pub async fn clear_bindings( ClearBindingsParams { except }: ClearBindingsParams, ) -> Result<(), Error> { let context = context.deref()?; - let mut svc = context.seed.persistent_container.net_service.lock().await; - svc.clear_bindings(except.into_iter().collect()).await?; + context + .seed + .persistent_container + .net_service + .clear_bindings(except.into_iter().collect()) + .await?; Ok(()) } diff --git a/core/startos/src/service/effects/net/info.rs b/core/startos/src/service/effects/net/info.rs index c33a1a81e..fe6623f44 100644 --- a/core/startos/src/service/effects/net/info.rs +++ b/core/startos/src/service/effects/net/info.rs @@ -4,6 +4,5 @@ use crate::service::effects::prelude::*; pub async fn get_container_ip(context: EffectContext) -> Result { let context = context.deref()?; - let net_service = context.seed.persistent_container.net_service.lock().await; - Ok(net_service.get_ip()) + Ok(context.seed.persistent_container.net_service.get_ip().await) } diff --git a/core/startos/src/service/mod.rs b/core/startos/src/service/mod.rs index 5bc7fd999..2a18ea518 100644 --- a/core/startos/src/service/mod.rs +++ b/core/startos/src/service/mod.rs @@ -605,23 +605,12 @@ impl Service { }) } - pub async fn update_host(&self, host_id: HostId) -> Result<(), Error> { - let mut service = self.seed.persistent_container.net_service.lock().await; - let host = self - .seed - .ctx - .db - .peek() + pub async fn sync_host(&self, host_id: HostId) -> Result<(), Error> { + self.seed + .persistent_container + .net_service + .sync_host(host_id) .await - .as_public() - .as_package_data() - .as_idx(&self.seed.id) - .or_not_found(&self.seed.id)? - .as_hosts() - .as_idx(&host_id) - .or_not_found(&host_id)? - .de()?; - service.update(host_id, host).await } } diff --git a/core/startos/src/service/persistent_container.rs b/core/startos/src/service/persistent_container.rs index d99d9bb49..910255e7d 100644 --- a/core/startos/src/service/persistent_container.rs +++ b/core/startos/src/service/persistent_container.rs @@ -110,7 +110,7 @@ pub struct PersistentContainer { pub(super) images: BTreeMap>, pub(super) subcontainers: Arc>>, pub(super) state: Arc>, - pub(super) net_service: Mutex, + pub(super) net_service: NetService, destroyed: bool, } @@ -285,7 +285,7 @@ impl PersistentContainer { images, subcontainers: Arc::new(Mutex::new(BTreeMap::new())), state: Arc::new(watch::channel(ServiceState::new(start)).0), - net_service: Mutex::new(net_service), + net_service, destroyed: false, }) } diff --git a/core/startos/src/service/service_actor.rs b/core/startos/src/service/service_actor.rs index bb30d2a98..a56c92288 100644 --- a/core/startos/src/service/service_actor.rs +++ b/core/startos/src/service/service_actor.rs @@ -37,38 +37,6 @@ impl Actor for ServiceActor { } } }); - let seed = self.0.clone(); - let mut ip_info = seed.ctx.net_controller.net_iface.subscribe(); - jobs.add_job(async move { - loop { - if let Err(e) = async { - let mut service = seed.persistent_container.net_service.lock().await; - let hosts = seed - .ctx - .db - .peek() - .await - .as_public() - .as_package_data() - .as_idx(&seed.id) - .or_not_found(&seed.id)? - .as_hosts() - .de()?; - for (host_id, host) in hosts.0 { - service.update(host_id, host).await?; - } - - Ok::<_, Error>(()) - } - .await - { - tracing::error!("Error syncronizing net host after network change: {e}"); - tracing::debug!("{e:?}"); - } - - ip_info.changed().await; - } - }); } } diff --git a/core/startos/src/service/service_map.rs b/core/startos/src/service/service_map.rs index 104e191ba..de450706e 100644 --- a/core/startos/src/service/service_map.rs +++ b/core/startos/src/service/service_map.rs @@ -4,7 +4,8 @@ use std::time::Duration; use color_eyre::eyre::eyre; use futures::future::BoxFuture; -use futures::{Future, FutureExt}; +use futures::stream::FuturesUnordered; +use futures::{Future, FutureExt, StreamExt}; use helpers::NonDetachingJoinHandle; use imbl::OrdMap; use imbl_value::InternedString; @@ -68,8 +69,12 @@ impl ServiceMap { progress.start(); let ids = ctx.db.peek().await.as_public().as_package_data().keys()?; progress.set_total(ids.len() as u64); - for id in ids { - if let Err(e) = self.load(ctx, &id, LoadDisposition::Retry).await { + let mut jobs = FuturesUnordered::new(); + for id in &ids { + jobs.push(self.load(ctx, id, LoadDisposition::Retry)); + } + while let Some(res) = jobs.next().await { + if let Err(e) = res { tracing::error!("Error loading installed package as service: {e}"); tracing::debug!("{e:?}"); } diff --git a/core/startos/src/setup.rs b/core/startos/src/setup.rs index e9109cabe..186fbb0c4 100644 --- a/core/startos/src/setup.rs +++ b/core/startos/src/setup.rs @@ -31,7 +31,7 @@ use crate::disk::mount::guard::{GenericMountGuard, TmpMountGuard}; use crate::disk::util::{pvscan, recovery_info, DiskInfo, StartOsRecoveryInfo}; use crate::disk::REPAIR_DISK_PATH; use crate::init::{init, InitPhases, InitResult}; -use crate::net::net_controller::PreInitNetController; +use crate::net::net_controller::NetController; use crate::net::ssl::root_ca_start_time; use crate::prelude::*; use crate::progress::{FullProgress, PhaseProgressTrackerHandle}; @@ -80,10 +80,11 @@ async fn setup_init( ctx: &SetupContext, password: Option, init_phases: InitPhases, -) -> Result<(AccountInfo, PreInitNetController), Error> { - let InitResult { net_ctrl } = init(&ctx.webserver, &ctx.config, init_phases).await?; +) -> Result<(AccountInfo, InitResult), Error> { + let init_result = init(&ctx.webserver, &ctx.config, init_phases).await?; - let account = net_ctrl + let account = init_result + .net_ctrl .db .mutate(|m| { let mut account = AccountInfo::load(m)?; @@ -99,7 +100,7 @@ async fn setup_init( }) .await?; - Ok((account, net_ctrl)) + Ok((account, init_result)) } #[derive(Deserialize, Serialize, TS)] @@ -452,13 +453,13 @@ async fn fresh_setup( db.put(&ROOT, &Database::init(&account)?).await?; drop(db); - let InitResult { net_ctrl } = init(&ctx.webserver, &ctx.config, init_phases).await?; + let init_result = init(&ctx.webserver, &ctx.config, init_phases).await?; let rpc_ctx = RpcContext::init( &ctx.webserver, &ctx.config, guid, - Some(net_ctrl), + Some(init_result), rpc_ctx_phases, ) .await?; diff --git a/core/startos/src/util/sync.rs b/core/startos/src/util/sync.rs index ca7b4c6bb..d6099cb5b 100644 --- a/core/startos/src/util/sync.rs +++ b/core/startos/src/util/sync.rs @@ -77,6 +77,14 @@ impl Watch { pub async fn changed(&mut self) { futures::future::poll_fn(|cx| self.poll_changed(cx)).await } + pub async fn wait_for bool>(&mut self, mut f: F) { + loop { + if self.peek(&mut f) { + break; + } + self.changed().await; + } + } pub fn send_if_modified bool>(&self, modify: F) -> bool { self.shared.mutate(|shared| { let changed = modify(&mut shared.data); diff --git a/core/startos/src/version/mod.rs b/core/startos/src/version/mod.rs index 3c1687ea9..966379470 100644 --- a/core/startos/src/version/mod.rs +++ b/core/startos/src/version/mod.rs @@ -30,8 +30,9 @@ mod v0_3_6_alpha_9; mod v0_3_6_alpha_10; mod v0_3_6_alpha_11; +mod v0_3_6_alpha_12; -pub type Current = v0_3_6_alpha_11::Version; // VERSION_BUMP +pub type Current = v0_3_6_alpha_12::Version; // VERSION_BUMP impl Current { #[instrument(skip(self, db))] @@ -113,6 +114,7 @@ enum Version { V0_3_6_alpha_9(Wrapper), V0_3_6_alpha_10(Wrapper), V0_3_6_alpha_11(Wrapper), + V0_3_6_alpha_12(Wrapper), Other(exver::Version), } @@ -148,6 +150,7 @@ impl Version { Self::V0_3_6_alpha_9(v) => DynVersion(Box::new(v.0)), Self::V0_3_6_alpha_10(v) => DynVersion(Box::new(v.0)), Self::V0_3_6_alpha_11(v) => DynVersion(Box::new(v.0)), + Self::V0_3_6_alpha_12(v) => DynVersion(Box::new(v.0)), Self::Other(v) => { return Err(Error::new( eyre!("unknown version {v}"), @@ -175,6 +178,7 @@ impl Version { Version::V0_3_6_alpha_9(Wrapper(x)) => x.semver(), Version::V0_3_6_alpha_10(Wrapper(x)) => x.semver(), Version::V0_3_6_alpha_11(Wrapper(x)) => x.semver(), + Version::V0_3_6_alpha_12(Wrapper(x)) => x.semver(), Version::Other(x) => x.clone(), } } diff --git a/core/startos/src/version/v0_3_6_alpha_0.rs b/core/startos/src/version/v0_3_6_alpha_0.rs index 7c594a9f9..b7f2ba2e4 100644 --- a/core/startos/src/version/v0_3_6_alpha_0.rs +++ b/core/startos/src/version/v0_3_6_alpha_0.rs @@ -478,7 +478,7 @@ async fn previous_account_info(pg: &sqlx::Pool) -> Result>, _>("tor_key") .with_ctx(|_| (ErrorKind::Database, "tor_key"))? @@ -503,7 +503,7 @@ async fn previous_account_info(pg: &sqlx::Pool) -> Result Result { + Ok(()) + } + fn semver(self) -> exver::Version { + V0_3_6_alpha_12.clone() + } + fn compat(self) -> &'static VersionRange { + &V0_3_0_COMPAT + } + fn up(self, db: &mut Value, _: Self::PreUpRes) -> Result<(), Error> { + let bindings: BTreeMap = [( + 80, + json!({ + "enabled": false, + "options": { + "preferredExternalPort": 80, + "addSsl": { + "preferredExternalPort": 443, + "alpn": { "specified": [ "http/1.1", "h2" ] }, + }, + "secure": null, + }, + "net": { + "assignedPort": null, + "assignedSslPort": 443, + "public": false, + } + }), + )] + .into_iter() + .collect(); + let onion = db["public"]["serverInfo"]["onionAddress"].clone(); + db["public"]["serverInfo"]["host"] = json!({ + "bindings": bindings, + "onions": [onion], + "domains": {}, + "hostnameInfo": {}, + }); + + Ok(()) + } + fn down(self, _db: &mut Value) -> Result<(), Error> { + Ok(()) + } +} diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index 9a9ca8f48..000000000 --- a/package-lock.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "name": "start-os", - "lockfileVersion": 2, - "requires": true, - "packages": {} -} diff --git a/patch-db b/patch-db index 2600a784a..36eb59b79 160000 --- a/patch-db +++ b/patch-db @@ -1 +1 @@ -Subproject commit 2600a784a9899a6f8e0c9abe0bf4c4ce48cb85a9 +Subproject commit 36eb59b79efec614fb4f94ed925336e97881a2f7 diff --git a/sdk/base/lib/interfaces/Host.ts b/sdk/base/lib/interfaces/Host.ts index b90dc1c60..53419357f 100644 --- a/sdk/base/lib/interfaces/Host.ts +++ b/sdk/base/lib/interfaces/Host.ts @@ -77,19 +77,18 @@ type BindOptionsByKnownProtocol = preferredExternalPort?: number addSsl?: AddSslOptions } -export type BindOptionsByProtocol = BindOptionsByKnownProtocol | BindOptions - -export type HostKind = BindParams["kind"] +export type BindOptionsByProtocol = + | BindOptionsByKnownProtocol + | (BindOptions & { protocol: null }) const hasStringProtocol = object({ protocol: string, }).test -export class Host { +export class MultiHost { constructor( readonly options: { effects: Effects - kind: HostKind id: string }, ) {} @@ -113,7 +112,7 @@ export class Host { async bindPort( internalPort: number, options: BindOptionsByProtocol, - ): Promise> { + ): Promise { if (hasStringProtocol(options)) { return await this.bindPortForKnown(options, internalPort) } else { @@ -130,7 +129,6 @@ export class Host { }, ) { const binderOptions = { - kind: this.options.kind, id: this.options.id, internalPort, ...options, @@ -163,7 +161,6 @@ export class Host { const secure: Security | null = !protoInfo.secure ? null : { ssl: false } await this.options.effects.bind({ - kind: this.options.kind, id: this.options.id, internalPort, preferredExternalPort, @@ -190,21 +187,3 @@ function inObject( ): obj is { [K in Key]: unknown } { return key in obj } - -// export class StaticHost extends Host { -// constructor(options: { effects: Effects; id: string }) { -// super({ ...options, kind: "static" }) -// } -// } - -// export class SingleHost extends Host { -// constructor(options: { effects: Effects; id: string }) { -// super({ ...options, kind: "single" }) -// } -// } - -export class MultiHost extends Host { - constructor(options: { effects: Effects; id: string }) { - super({ ...options, kind: "multi" }) - } -} diff --git a/sdk/base/lib/interfaces/Origin.ts b/sdk/base/lib/interfaces/Origin.ts index 9985176e4..dd688b3c9 100644 --- a/sdk/base/lib/interfaces/Origin.ts +++ b/sdk/base/lib/interfaces/Origin.ts @@ -1,11 +1,11 @@ import { AddressInfo } from "../types" import { AddressReceipt } from "./AddressReceipt" -import { Host, Scheme } from "./Host" +import { MultiHost, Scheme } from "./Host" import { ServiceInterfaceBuilder } from "./ServiceInterfaceBuilder" -export class Origin { +export class Origin { constructor( - readonly host: T, + readonly host: MultiHost, readonly internalPort: number, readonly scheme: string | null, readonly sslScheme: string | null, diff --git a/sdk/base/lib/osBindings/BindParams.ts b/sdk/base/lib/osBindings/BindParams.ts index fcc450476..9064a33b1 100644 --- a/sdk/base/lib/osBindings/BindParams.ts +++ b/sdk/base/lib/osBindings/BindParams.ts @@ -1,11 +1,9 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. import type { AddSslOptions } from "./AddSslOptions" import type { HostId } from "./HostId" -import type { HostKind } from "./HostKind" import type { Security } from "./Security" export type BindParams = { - kind: HostKind id: HostId internalPort: number preferredExternalPort: number diff --git a/sdk/base/lib/osBindings/Host.ts b/sdk/base/lib/osBindings/Host.ts index 5436e9955..041e1c9bc 100644 --- a/sdk/base/lib/osBindings/Host.ts +++ b/sdk/base/lib/osBindings/Host.ts @@ -1,11 +1,9 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. import type { BindInfo } from "./BindInfo" import type { DomainConfig } from "./DomainConfig" -import type { HostKind } from "./HostKind" import type { HostnameInfo } from "./HostnameInfo" export type Host = { - kind: HostKind bindings: { [key: number]: BindInfo } onions: string[] domains: { [key: string]: DomainConfig } diff --git a/sdk/base/lib/osBindings/HostKind.ts b/sdk/base/lib/osBindings/HostKind.ts deleted file mode 100644 index 3c2f5ad87..000000000 --- a/sdk/base/lib/osBindings/HostKind.ts +++ /dev/null @@ -1,3 +0,0 @@ -// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. - -export type HostKind = "multi" diff --git a/sdk/base/lib/osBindings/ServerInfo.ts b/sdk/base/lib/osBindings/ServerInfo.ts index 6eec14745..5779fa7d5 100644 --- a/sdk/base/lib/osBindings/ServerInfo.ts +++ b/sdk/base/lib/osBindings/ServerInfo.ts @@ -2,6 +2,7 @@ import type { AcmeProvider } from "./AcmeProvider" import type { AcmeSettings } from "./AcmeSettings" import type { Governor } from "./Governor" +import type { Host } from "./Host" import type { LshwDevice } from "./LshwDevice" import type { NetworkInterfaceInfo } from "./NetworkInterfaceInfo" import type { ServerStatus } from "./ServerStatus" @@ -13,16 +14,11 @@ export type ServerInfo = { platform: string id: string hostname: string + host: Host version: string packageVersionCompat: string postInitMigrationTodos: string[] lastBackup: string | null - lanAddress: string - onionAddress: string - /** - * for backwards compatibility - */ - torAddress: string networkInterfaces: { [key: string]: NetworkInterfaceInfo } acme: { [key: AcmeProvider]: AcmeSettings } statusInfo: ServerStatus diff --git a/sdk/base/lib/osBindings/SetupResult.ts b/sdk/base/lib/osBindings/SetupResult.ts index d81c7f039..3147187c1 100644 --- a/sdk/base/lib/osBindings/SetupResult.ts +++ b/sdk/base/lib/osBindings/SetupResult.ts @@ -1,7 +1,7 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. export type SetupResult = { - torAddress: string + torAddresses: Array hostname: string lanAddress: string rootCa: string diff --git a/sdk/base/lib/osBindings/index.ts b/sdk/base/lib/osBindings/index.ts index 3c16f6e70..8ba83c9e0 100644 --- a/sdk/base/lib/osBindings/index.ts +++ b/sdk/base/lib/osBindings/index.ts @@ -4,13 +4,13 @@ export { AcmeSettings } from "./AcmeSettings" export { ActionId } from "./ActionId" export { ActionInput } from "./ActionInput" export { ActionMetadata } from "./ActionMetadata" +export { ActionRequest } from "./ActionRequest" export { ActionRequestCondition } from "./ActionRequestCondition" export { ActionRequestEntry } from "./ActionRequestEntry" export { ActionRequestInput } from "./ActionRequestInput" export { ActionRequestTrigger } from "./ActionRequestTrigger" -export { ActionRequest } from "./ActionRequest" -export { ActionResultMember } from "./ActionResultMember" export { ActionResult } from "./ActionResult" +export { ActionResultMember } from "./ActionResultMember" export { ActionResultV0 } from "./ActionResultV0" export { ActionResultV1 } from "./ActionResultV1" export { ActionResultValue } from "./ActionResultValue" @@ -20,13 +20,13 @@ export { AddAdminParams } from "./AddAdminParams" export { AddAssetParams } from "./AddAssetParams" export { AddCategoryParams } from "./AddCategoryParams" export { AddPackageParams } from "./AddPackageParams" -export { AddressInfo } from "./AddressInfo" export { AddSslOptions } from "./AddSslOptions" export { AddVersionParams } from "./AddVersionParams" +export { AddressInfo } from "./AddressInfo" export { Alerts } from "./Alerts" export { Algorithm } from "./Algorithm" -export { AllowedStatuses } from "./AllowedStatuses" export { AllPackageData } from "./AllPackageData" +export { AllowedStatuses } from "./AllowedStatuses" export { AlpnInfo } from "./AlpnInfo" export { AnySignature } from "./AnySignature" export { AnySigningKey } from "./AnySigningKey" @@ -38,9 +38,9 @@ export { BackupTargetFS } from "./BackupTargetFS" export { Base64 } from "./Base64" export { BindId } from "./BindId" export { BindInfo } from "./BindInfo" -export { BindingSetPublicParams } from "./BindingSetPublicParams" export { BindOptions } from "./BindOptions" export { BindParams } from "./BindParams" +export { BindingSetPublicParams } from "./BindingSetPublicParams" export { Blake3Commitment } from "./Blake3Commitment" export { BlockDev } from "./BlockDev" export { BuildArg } from "./BuildArg" @@ -60,11 +60,11 @@ export { CreateSubcontainerFsParams } from "./CreateSubcontainerFsParams" export { CurrentDependencies } from "./CurrentDependencies" export { CurrentDependencyInfo } from "./CurrentDependencyInfo" export { DataUrl } from "./DataUrl" +export { DepInfo } from "./DepInfo" export { Dependencies } from "./Dependencies" export { DependencyKind } from "./DependencyKind" export { DependencyMetadata } from "./DependencyMetadata" export { DependencyRequirement } from "./DependencyRequirement" -export { DepInfo } from "./DepInfo" export { Description } from "./Description" export { DestroySubcontainerFsParams } from "./DestroySubcontainerFsParams" export { DeviceFilter } from "./DeviceFilter" @@ -84,8 +84,8 @@ export { GetHostInfoParams } from "./GetHostInfoParams" export { GetOsAssetParams } from "./GetOsAssetParams" export { GetOsVersionParams } from "./GetOsVersionParams" export { GetPackageParams } from "./GetPackageParams" -export { GetPackageResponseFull } from "./GetPackageResponseFull" export { GetPackageResponse } from "./GetPackageResponse" +export { GetPackageResponseFull } from "./GetPackageResponseFull" export { GetServiceInterfaceParams } from "./GetServiceInterfaceParams" export { GetServicePortForwardParams } from "./GetServicePortForwardParams" export { GetSslCertificateParams } from "./GetSslCertificateParams" @@ -98,22 +98,21 @@ export { Governor } from "./Governor" export { Guid } from "./Guid" export { HardwareRequirements } from "./HardwareRequirements" export { HealthCheckId } from "./HealthCheckId" +export { Host } from "./Host" export { HostAddress } from "./HostAddress" export { HostId } from "./HostId" -export { HostKind } from "./HostKind" export { HostnameInfo } from "./HostnameInfo" export { Hosts } from "./Hosts" -export { Host } from "./Host" export { ImageConfig } from "./ImageConfig" export { ImageId } from "./ImageId" export { ImageMetadata } from "./ImageMetadata" export { ImageSource } from "./ImageSource" export { InitProgressRes } from "./InitProgressRes" +export { InstallParams } from "./InstallParams" export { InstalledState } from "./InstalledState" export { InstalledVersionParams } from "./InstalledVersionParams" export { InstallingInfo } from "./InstallingInfo" export { InstallingState } from "./InstallingState" -export { InstallParams } from "./InstallParams" export { IpHostname } from "./IpHostname" export { IpInfo } from "./IpInfo" export { ListPackageSignersParams } from "./ListPackageSignersParams" @@ -137,14 +136,14 @@ export { NetworkInterfaceSetPublicParams } from "./NetworkInterfaceSetPublicPara export { NetworkInterfaceType } from "./NetworkInterfaceType" export { OnionHostname } from "./OnionHostname" export { OsIndex } from "./OsIndex" -export { OsVersionInfoMap } from "./OsVersionInfoMap" export { OsVersionInfo } from "./OsVersionInfo" +export { OsVersionInfoMap } from "./OsVersionInfoMap" export { PackageDataEntry } from "./PackageDataEntry" export { PackageDetailLevel } from "./PackageDetailLevel" export { PackageId } from "./PackageId" export { PackageIndex } from "./PackageIndex" -export { PackageInfoShort } from "./PackageInfoShort" export { PackageInfo } from "./PackageInfo" +export { PackageInfoShort } from "./PackageInfoShort" export { PackageSignerParams } from "./PackageSignerParams" export { PackageState } from "./PackageState" export { PackageVersionInfo } from "./PackageVersionInfo" @@ -166,18 +165,18 @@ export { Security } from "./Security" export { ServerInfo } from "./ServerInfo" export { ServerSpecs } from "./ServerSpecs" export { ServerStatus } from "./ServerStatus" -export { ServiceInterfaceId } from "./ServiceInterfaceId" export { ServiceInterface } from "./ServiceInterface" +export { ServiceInterfaceId } from "./ServiceInterfaceId" export { ServiceInterfaceType } from "./ServiceInterfaceType" +export { Session } from "./Session" export { SessionList } from "./SessionList" export { Sessions } from "./Sessions" -export { Session } from "./Session" export { SetDataVersionParams } from "./SetDataVersionParams" export { SetDependenciesParams } from "./SetDependenciesParams" export { SetHealth } from "./SetHealth" export { SetIconParams } from "./SetIconParams" -export { SetMainStatusStatus } from "./SetMainStatusStatus" export { SetMainStatus } from "./SetMainStatus" +export { SetMainStatusStatus } from "./SetMainStatusStatus" export { SetNameParams } from "./SetNameParams" export { SetStoreParams } from "./SetStoreParams" export { SetupExecuteParams } from "./SetupExecuteParams" @@ -192,7 +191,7 @@ export { TestSmtpParams } from "./TestSmtpParams" export { UnsetPublicParams } from "./UnsetPublicParams" export { UpdatingState } from "./UpdatingState" export { VerifyCifsParams } from "./VerifyCifsParams" -export { VersionSignerParams } from "./VersionSignerParams" export { Version } from "./Version" +export { VersionSignerParams } from "./VersionSignerParams" export { VolumeId } from "./VolumeId" export { WifiInfo } from "./WifiInfo" diff --git a/sdk/base/lib/util/patterns.ts b/sdk/base/lib/util/patterns.ts index 46f67b6b8..a61e4269c 100644 --- a/sdk/base/lib/util/patterns.ts +++ b/sdk/base/lib/util/patterns.ts @@ -2,63 +2,68 @@ import { Pattern } from "../actions/input/inputSpecTypes" import * as regexes from "./regexes" export const ipv6: Pattern = { - regex: regexes.ipv6.source, + regex: regexes.ipv6.matches(), description: "Must be a valid IPv6 address", } export const ipv4: Pattern = { - regex: regexes.ipv4.source, + regex: regexes.ipv4.matches(), description: "Must be a valid IPv4 address", } export const hostname: Pattern = { - regex: regexes.hostname.source, + regex: regexes.hostname.matches(), description: "Must be a valid hostname", } export const localHostname: Pattern = { - regex: regexes.localHostname.source, + regex: regexes.localHostname.matches(), description: 'Must be a valid ".local" hostname', } export const torHostname: Pattern = { - regex: regexes.torHostname.source, + regex: regexes.torHostname.matches(), description: 'Must be a valid Tor (".onion") hostname', } export const url: Pattern = { - regex: regexes.url.source, + regex: regexes.url.matches(), description: "Must be a valid URL", } export const localUrl: Pattern = { - regex: regexes.localUrl.source, + regex: regexes.localUrl.matches(), description: 'Must be a valid ".local" URL', } export const torUrl: Pattern = { - regex: regexes.torUrl.source, + regex: regexes.torUrl.matches(), description: 'Must be a valid Tor (".onion") URL', } export const ascii: Pattern = { - regex: regexes.ascii.source, + regex: regexes.ascii.matches(), description: "May only contain ASCII characters. See https://www.w3schools.com/charsets/ref_html_ascii.asp", } +export const domain: Pattern = { + regex: regexes.domain.matches(), + description: "Must be a valid Fully Qualified Domain Name", +} + export const email: Pattern = { - regex: regexes.email.source, + regex: regexes.email.matches(), description: "Must be a valid email address", } export const emailWithName: Pattern = { - regex: regexes.emailWithName.source, + regex: regexes.emailWithName.matches(), description: "Must be a valid email address, optionally with a name", } export const base64: Pattern = { - regex: regexes.base64.source, + regex: regexes.base64.matches(), description: "May only contain base64 characters. See https://base64.guru/learn/base64-characters", } diff --git a/sdk/base/lib/util/regexes.ts b/sdk/base/lib/util/regexes.ts index e4e11766a..a2a8cde7a 100644 --- a/sdk/base/lib/util/regexes.ts +++ b/sdk/base/lib/util/regexes.ts @@ -1,38 +1,71 @@ +export class ComposableRegex { + readonly regex: RegExp + constructor(regex: RegExp | string) { + if (regex instanceof RegExp) { + this.regex = regex + } else { + this.regex = new RegExp(regex) + } + } + asExpr(): string { + return `(${this.regex.source})` + } + matches(): string { + return `^${this.regex.source}$` + } + contains(): string { + return this.regex.source + } +} + // https://ihateregex.io/expr/ipv6/ -export const ipv6 = - /(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))/ +export const ipv6 = new ComposableRegex( + /(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))/, +) // https://ihateregex.io/expr/ipv4/ -export const ipv4 = - /(\b25[0-5]|\b2[0-4][0-9]|\b[01]?[0-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}/ +export const ipv4 = new ComposableRegex( + /(\b25[0-5]|\b2[0-4][0-9]|\b[01]?[0-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}/, +) -export const hostname = - /^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$/ +export const hostname = new ComposableRegex( + /(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])/, +) -export const localHostname = /[-a-zA-Z0-9@:%._\+~#=]{1,256}\.local/ +export const localHostname = new ComposableRegex( + /[-a-zA-Z0-9@:%._\+~#=]{1,256}\.local/, +) -export const torHostname = /[-a-zA-Z0-9@:%._\+~#=]{1,256}\.onion/ +export const torHostname = new ComposableRegex( + /[-a-zA-Z0-9@:%._\+~#=]{1,256}\.onion/, +) // https://ihateregex.io/expr/url/ -export const url = - /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()!@:%_\+.~#?&\/\/=]*)/ +export const url = new ComposableRegex( + /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()!@:%_\+.~#?&\/\/=]*)/, +) -export const localUrl = - /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.local\b([-a-zA-Z0-9()!@:%_\+.~#?&\/\/=]*)/ +export const localUrl = new ComposableRegex( + /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.local\b([-a-zA-Z0-9()!@:%_\+.~#?&\/\/=]*)/, +) -export const torUrl = - /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.onion\b([-a-zA-Z0-9()!@:%_\+.~#?&\/\/=]*)/ +export const torUrl = new ComposableRegex( + /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.onion\b([-a-zA-Z0-9()!@:%_\+.~#?&\/\/=]*)/, +) // https://ihateregex.io/expr/ascii/ -export const ascii = /^[ -~]*$/ +export const ascii = new ComposableRegex(/[ -~]*/) + +export const domain = new ComposableRegex(/[A-Za-z0-9.-]+\.[A-Za-z]{2,}/) // https://www.regular-expressions.info/email.html -export const email = /[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}/ +export const email = new ComposableRegex(`[A-Za-z0-9._%+-]+@${domain.asExpr()}`) -export const emailWithName = new RegExp( - `(${email.source})|([^<]*<(${email.source})>)`, +export const emailWithName = new ComposableRegex( + `${email.asExpr()}|([^<]*<${email.asExpr()}>)`, ) //https://rgxdb.com/r/1NUN74O6 -export const base64 = - /^(?:[a-zA-Z0-9+\/]{4})*(?:|(?:[a-zA-Z0-9+\/]{3}=)|(?:[a-zA-Z0-9+\/]{2}==)|(?:[a-zA-Z0-9+\/]{1}===))$/ +export const base64 = new ComposableRegex( + /(?:[a-zA-Z0-9+\/]{4})*(?:|(?:[a-zA-Z0-9+\/]{3}=)|(?:[a-zA-Z0-9+\/]{2}==)|(?:[a-zA-Z0-9+\/]{1}===))/, +) diff --git a/system-images/compat/Cargo.toml b/system-images/compat/Cargo.toml index e527c4150..d5fb24161 100644 --- a/system-images/compat/Cargo.toml +++ b/system-images/compat/Cargo.toml @@ -17,7 +17,7 @@ emver = { version = "0.1.7", git = "https://github.com/Start9Labs/emver-rs.git", ] } failure = "0.1.8" indexmap = { version = "1.6.2", features = ["serde"] } -imbl-value = { git = "https://github.com/Start9Labs/imbl-value.git" } +imbl-value = "0.1.2" itertools = "0.10.0" lazy_static = "1.4" linear-map = { version = "1.2", features = ["serde_impl"] } diff --git a/web/package-lock.json b/web/package-lock.json index 740020a3a..f218a6d82 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -1,12 +1,12 @@ { "name": "startos-ui", - "version": "0.3.6-alpha.11", + "version": "0.3.6-alpha.12", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "startos-ui", - "version": "0.3.6-alpha.11", + "version": "0.3.6-alpha.12", "license": "MIT", "dependencies": { "@angular/animations": "^14.1.0", @@ -126,7 +126,7 @@ "isomorphic-fetch": "^3.0.0", "lodash.merge": "^4.6.2", "mime-types": "^2.1.35", - "ts-matches": "^6.1.0", + "ts-matches": "^6.2.1", "yaml": "^2.2.2" }, "devDependencies": { diff --git a/web/package.json b/web/package.json index 800297407..3355c46cc 100644 --- a/web/package.json +++ b/web/package.json @@ -1,6 +1,6 @@ { "name": "startos-ui", - "version": "0.3.6-alpha.11", + "version": "0.3.6-alpha.12", "author": "Start9 Labs, Inc", "homepage": "https://start9.com/", "license": "MIT", diff --git a/web/projects/setup-wizard/src/app/pages/success/download-doc/download-doc.component.html b/web/projects/setup-wizard/src/app/pages/success/download-doc/download-doc.component.html index ef8c32a49..60e25e48a 100644 --- a/web/projects/setup-wizard/src/app/pages/success/download-doc/download-doc.component.html +++ b/web/projects/setup-wizard/src/app/pages/success/download-doc/download-doc.component.html @@ -121,7 +121,7 @@

overflow: auto; " > - +

diff --git a/web/projects/setup-wizard/src/app/pages/success/success.page.ts b/web/projects/setup-wizard/src/app/pages/success/success.page.ts index aa0c7fc90..198ec3387 100644 --- a/web/projects/setup-wizard/src/app/pages/success/success.page.ts +++ b/web/projects/setup-wizard/src/app/pages/success/success.page.ts @@ -15,7 +15,7 @@ export class SuccessPage { {} as ElementRef private ctx: CanvasRenderingContext2D = {} as CanvasRenderingContext2D - torAddress?: string + torAddresses?: string[] lanAddress?: string cert?: string @@ -52,7 +52,7 @@ export class SuccessPage { const torAddress = this.document.getElementById('tor-addr') const lanAddress = this.document.getElementById('lan-addr') - if (torAddress) torAddress.innerHTML = this.torAddress! + if (torAddress) torAddress.innerHTML = this.torAddresses!.join('\n') if (lanAddress) lanAddress.innerHTML = this.lanAddress! this.document @@ -76,7 +76,9 @@ export class SuccessPage { try { const ret = await this.api.complete() if (!this.isKiosk) { - this.torAddress = ret.torAddress.replace(/^https:/, 'http:') + this.torAddresses = ret.torAddresses.map(a => + a.replace(/^https:/, 'http:'), + ) this.lanAddress = ret.lanAddress.replace(/^https:/, 'http:') this.cert = ret.rootCa diff --git a/web/projects/setup-wizard/src/app/services/api/mock-api.service.ts b/web/projects/setup-wizard/src/app/services/api/mock-api.service.ts index a17f56a0c..e84ef5335 100644 --- a/web/projects/setup-wizard/src/app/services/api/mock-api.service.ts +++ b/web/projects/setup-wizard/src/app/services/api/mock-api.service.ts @@ -136,7 +136,7 @@ export class MockApiService extends ApiService { case 3: return { status: 'complete', - torAddress: 'https://asdafsadasdasasdasdfasdfasdf.onion', + torAddresses: ['https://asdafsadasdasasdasdfasdfasdf.onion'], hostname: 'adjective-noun', lanAddress: 'https://adjective-noun.local', rootCa: encodeBase64(rootCA), @@ -283,7 +283,7 @@ export class MockApiService extends ApiService { async complete(): Promise { await pauseFor(1000) return { - torAddress: 'https://asdafsadasdasasdasdfasdfasdf.onion', + torAddresses: ['https://asdafsadasdasasdasdfasdfasdf.onion'], hostname: 'adjective-noun', lanAddress: 'https://adjective-noun.local', rootCa: encodeBase64(rootCA), diff --git a/web/projects/shared/src/types/workspace-config.ts b/web/projects/shared/src/types/workspace-config.ts index d10c4b07e..0586b3560 100644 --- a/web/projects/shared/src/types/workspace-config.ts +++ b/web/projects/shared/src/types/workspace-config.ts @@ -13,7 +13,7 @@ export type WorkspaceConfig = { community: 'https://community-registry.start9.com/' } mocks: { - maskAs: 'tor' | 'local' | 'ip' | 'localhost' + maskAs: 'tor' | 'local' | 'localhost' | 'ipv4' | 'ipv6' | 'clearnet' // enables local development in secure mode maskAsHttps: boolean skipStartupAlerts: boolean diff --git a/web/projects/ui/src/app/app/preloader/preloader.component.ts b/web/projects/ui/src/app/app/preloader/preloader.component.ts index 70c5e2995..772c5c40b 100644 --- a/web/projects/ui/src/app/app/preloader/preloader.component.ts +++ b/web/projects/ui/src/app/app/preloader/preloader.component.ts @@ -45,7 +45,7 @@ const ICONS = [ 'eye-off-outline', 'eye-outline', 'file-tray-stacked-outline', - 'finger-print-outline', + 'finger-print', 'flash-outline', 'folder-open-outline', 'globe-outline', diff --git a/web/projects/ui/src/app/components/form/form-select/form-select.component.html b/web/projects/ui/src/app/components/form/form-select/form-select.component.html index 9149e8844..b8e79351a 100644 --- a/web/projects/ui/src/app/components/form/form-select/form-select.component.html +++ b/web/projects/ui/src/app/components/form/form-select/form-select.component.html @@ -7,7 +7,7 @@ [(ngModel)]="selected" (focusedChange)="onFocus($event)" > - {{ spec.name }}* + {{ spec.name }} *