From cd2d4e06e5d5fbb54e9ed6ac341329d977ba59bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20Schm=C3=B6cker?= Date: Tue, 1 Oct 2024 19:35:13 +0200 Subject: [PATCH 01/13] feat: added terminal --- controller/poetry.lock | 228 +++++++++++++++++- controller/pyproject.toml | 1 + controller/thymis_controller/routers/api.py | 46 +++- controller/thymis_controller/tcp_to_ws.py | 36 +++ frontend/package-lock.json | 226 +++++++++++++++++ frontend/package.json | 1 + .../(authenticated)/terminal/+page.svelte | 37 +++ 7 files changed, 572 insertions(+), 3 deletions(-) diff --git a/controller/poetry.lock b/controller/poetry.lock index 47e450b4..ed2a8e1a 100644 --- a/controller/poetry.lock +++ b/controller/poetry.lock @@ -50,6 +50,46 @@ doc = ["Sphinx (>=7)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphin test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"] trio = ["trio (>=0.23)"] +[[package]] +name = "bcrypt" +version = "4.2.0" +description = "Modern password hashing for your software and your servers" +optional = false +python-versions = ">=3.7" +files = [ + {file = "bcrypt-4.2.0-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:096a15d26ed6ce37a14c1ac1e48119660f21b24cba457f160a4b830f3fe6b5cb"}, + {file = "bcrypt-4.2.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c02d944ca89d9b1922ceb8a46460dd17df1ba37ab66feac4870f6862a1533c00"}, + {file = "bcrypt-4.2.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1d84cf6d877918620b687b8fd1bf7781d11e8a0998f576c7aa939776b512b98d"}, + {file = "bcrypt-4.2.0-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:1bb429fedbe0249465cdd85a58e8376f31bb315e484f16e68ca4c786dcc04291"}, + {file = "bcrypt-4.2.0-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:655ea221910bcac76ea08aaa76df427ef8625f92e55a8ee44fbf7753dbabb328"}, + {file = "bcrypt-4.2.0-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:1ee38e858bf5d0287c39b7a1fc59eec64bbf880c7d504d3a06a96c16e14058e7"}, + {file = "bcrypt-4.2.0-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:0da52759f7f30e83f1e30a888d9163a81353ef224d82dc58eb5bb52efcabc399"}, + {file = "bcrypt-4.2.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:3698393a1b1f1fd5714524193849d0c6d524d33523acca37cd28f02899285060"}, + {file = "bcrypt-4.2.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:762a2c5fb35f89606a9fde5e51392dad0cd1ab7ae64149a8b935fe8d79dd5ed7"}, + {file = "bcrypt-4.2.0-cp37-abi3-win32.whl", hash = "sha256:5a1e8aa9b28ae28020a3ac4b053117fb51c57a010b9f969603ed885f23841458"}, + {file = "bcrypt-4.2.0-cp37-abi3-win_amd64.whl", hash = "sha256:8f6ede91359e5df88d1f5c1ef47428a4420136f3ce97763e31b86dd8280fbdf5"}, + {file = "bcrypt-4.2.0-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:c52aac18ea1f4a4f65963ea4f9530c306b56ccd0c6f8c8da0c06976e34a6e841"}, + {file = "bcrypt-4.2.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3bbbfb2734f0e4f37c5136130405332640a1e46e6b23e000eeff2ba8d005da68"}, + {file = "bcrypt-4.2.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3413bd60460f76097ee2e0a493ccebe4a7601918219c02f503984f0a7ee0aebe"}, + {file = "bcrypt-4.2.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:8d7bb9c42801035e61c109c345a28ed7e84426ae4865511eb82e913df18f58c2"}, + {file = "bcrypt-4.2.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3d3a6d28cb2305b43feac298774b997e372e56c7c7afd90a12b3dc49b189151c"}, + {file = "bcrypt-4.2.0-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:9c1c4ad86351339c5f320ca372dfba6cb6beb25e8efc659bedd918d921956bae"}, + {file = "bcrypt-4.2.0-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:27fe0f57bb5573104b5a6de5e4153c60814c711b29364c10a75a54bb6d7ff48d"}, + {file = "bcrypt-4.2.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:8ac68872c82f1add6a20bd489870c71b00ebacd2e9134a8aa3f98a0052ab4b0e"}, + {file = "bcrypt-4.2.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:cb2a8ec2bc07d3553ccebf0746bbf3d19426d1c6d1adbd4fa48925f66af7b9e8"}, + {file = "bcrypt-4.2.0-cp39-abi3-win32.whl", hash = "sha256:77800b7147c9dc905db1cba26abe31e504d8247ac73580b4aa179f98e6608f34"}, + {file = "bcrypt-4.2.0-cp39-abi3-win_amd64.whl", hash = "sha256:61ed14326ee023917ecd093ee6ef422a72f3aec6f07e21ea5f10622b735538a9"}, + {file = "bcrypt-4.2.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:39e1d30c7233cfc54f5c3f2c825156fe044efdd3e0b9d309512cc514a263ec2a"}, + {file = "bcrypt-4.2.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:f4f4acf526fcd1c34e7ce851147deedd4e26e6402369304220250598b26448db"}, + {file = "bcrypt-4.2.0-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:1ff39b78a52cf03fdf902635e4c81e544714861ba3f0efc56558979dd4f09170"}, + {file = "bcrypt-4.2.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:373db9abe198e8e2c70d12b479464e0d5092cc122b20ec504097b5f2297ed184"}, + {file = "bcrypt-4.2.0.tar.gz", hash = "sha256:cf69eaf5185fd58f268f805b505ce31f9b9fc2d64b376642164e9244540c1221"}, +] + +[package.extras] +tests = ["pytest (>=3.2.1,!=3.3.0)"] +typecheck = ["mypy"] + [[package]] name = "certifi" version = "2024.8.30" @@ -61,6 +101,85 @@ files = [ {file = "certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9"}, ] +[[package]] +name = "cffi" +version = "1.17.1" +description = "Foreign Function Interface for Python calling C code." +optional = false +python-versions = ">=3.8" +files = [ + {file = "cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14"}, + {file = "cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:edae79245293e15384b51f88b00613ba9f7198016a5948b5dddf4917d4d26382"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45398b671ac6d70e67da8e4224a065cec6a93541bb7aebe1b198a61b58c7b702"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad9413ccdeda48c5afdae7e4fa2192157e991ff761e7ab8fdd8926f40b160cc3"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5da5719280082ac6bd9aa7becb3938dc9f9cbd57fac7d2871717b1feb0902ab6"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bb1a08b8008b281856e5971307cc386a8e9c5b625ac297e853d36da6efe9c17"}, + {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:045d61c734659cc045141be4bae381a41d89b741f795af1dd018bfb532fd0df8"}, + {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6883e737d7d9e4899a8a695e00ec36bd4e5e4f18fabe0aca0efe0a4b44cdb13e"}, + {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6b8b4a92e1c65048ff98cfe1f735ef8f1ceb72e3d5f0c25fdb12087a23da22be"}, + {file = "cffi-1.17.1-cp310-cp310-win32.whl", hash = "sha256:c9c3d058ebabb74db66e431095118094d06abf53284d9c81f27300d0e0d8bc7c"}, + {file = "cffi-1.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:0f048dcf80db46f0098ccac01132761580d28e28bc0f78ae0d58048063317e15"}, + {file = "cffi-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401"}, + {file = "cffi-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d"}, + {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6"}, + {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f"}, + {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b"}, + {file = "cffi-1.17.1-cp311-cp311-win32.whl", hash = "sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655"}, + {file = "cffi-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0"}, + {file = "cffi-1.17.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4"}, + {file = "cffi-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93"}, + {file = "cffi-1.17.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3"}, + {file = "cffi-1.17.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8"}, + {file = "cffi-1.17.1-cp312-cp312-win32.whl", hash = "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65"}, + {file = "cffi-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903"}, + {file = "cffi-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e"}, + {file = "cffi-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd"}, + {file = "cffi-1.17.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed"}, + {file = "cffi-1.17.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9"}, + {file = "cffi-1.17.1-cp313-cp313-win32.whl", hash = "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d"}, + {file = "cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a"}, + {file = "cffi-1.17.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:636062ea65bd0195bc012fea9321aca499c0504409f413dc88af450b57ffd03b"}, + {file = "cffi-1.17.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7eac2ef9b63c79431bc4b25f1cd649d7f061a28808cbc6c47b534bd789ef964"}, + {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e221cf152cff04059d011ee126477f0d9588303eb57e88923578ace7baad17f9"}, + {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:31000ec67d4221a71bd3f67df918b1f88f676f1c3b535a7eb473255fdc0b83fc"}, + {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6f17be4345073b0a7b8ea599688f692ac3ef23ce28e5df79c04de519dbc4912c"}, + {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2b1fac190ae3ebfe37b979cc1ce69c81f4e4fe5746bb401dca63a9062cdaf1"}, + {file = "cffi-1.17.1-cp38-cp38-win32.whl", hash = "sha256:7596d6620d3fa590f677e9ee430df2958d2d6d6de2feeae5b20e82c00b76fbf8"}, + {file = "cffi-1.17.1-cp38-cp38-win_amd64.whl", hash = "sha256:78122be759c3f8a014ce010908ae03364d00a1f81ab5c7f4a7a5120607ea56e1"}, + {file = "cffi-1.17.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b2ab587605f4ba0bf81dc0cb08a41bd1c0a5906bd59243d56bad7668a6fc6c16"}, + {file = "cffi-1.17.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:28b16024becceed8c6dfbc75629e27788d8a3f9030691a1dbf9821a128b22c36"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1d599671f396c4723d016dbddb72fe8e0397082b0a77a4fab8028923bec050e8"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca74b8dbe6e8e8263c0ffd60277de77dcee6c837a3d0881d8c1ead7268c9e576"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7f5baafcc48261359e14bcd6d9bff6d4b28d9103847c9e136694cb0501aef87"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98e3969bcff97cae1b2def8ba499ea3d6f31ddfdb7635374834cf89a1a08ecf0"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cdf5ce3acdfd1661132f2a9c19cac174758dc2352bfe37d98aa7512c6b7178b3"}, + {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9755e4345d1ec879e3849e62222a18c7174d65a6a92d5b346b1863912168b595"}, + {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f1e22e8c4419538cb197e4dd60acc919d7696e5ef98ee4da4e01d3f8cfa4cc5a"}, + {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c03e868a0b3bc35839ba98e74211ed2b05d2119be4e8a0f224fba9384f1fe02e"}, + {file = "cffi-1.17.1-cp39-cp39-win32.whl", hash = "sha256:e31ae45bc2e29f6b2abd0de1cc3b9d5205aa847cafaecb8af1476a609a2f6eb7"}, + {file = "cffi-1.17.1-cp39-cp39-win_amd64.whl", hash = "sha256:d016c76bdd850f3c626af19b0542c9677ba156e4ee4fccfdd7848803533ef662"}, + {file = "cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824"}, +] + +[package.dependencies] +pycparser = "*" + [[package]] name = "click" version = "8.1.7" @@ -170,6 +289,55 @@ files = [ [package.extras] toml = ["tomli"] +[[package]] +name = "cryptography" +version = "43.0.1" +description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." +optional = false +python-versions = ">=3.7" +files = [ + {file = "cryptography-43.0.1-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:8385d98f6a3bf8bb2d65a73e17ed87a3ba84f6991c155691c51112075f9ffc5d"}, + {file = "cryptography-43.0.1-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:27e613d7077ac613e399270253259d9d53872aaf657471473ebfc9a52935c062"}, + {file = "cryptography-43.0.1-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:68aaecc4178e90719e95298515979814bda0cbada1256a4485414860bd7ab962"}, + {file = "cryptography-43.0.1-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:de41fd81a41e53267cb020bb3a7212861da53a7d39f863585d13ea11049cf277"}, + {file = "cryptography-43.0.1-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f98bf604c82c416bc829e490c700ca1553eafdf2912a91e23a79d97d9801372a"}, + {file = "cryptography-43.0.1-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:61ec41068b7b74268fa86e3e9e12b9f0c21fcf65434571dbb13d954bceb08042"}, + {file = "cryptography-43.0.1-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:014f58110f53237ace6a408b5beb6c427b64e084eb451ef25a28308270086494"}, + {file = "cryptography-43.0.1-cp37-abi3-win32.whl", hash = "sha256:2bd51274dcd59f09dd952afb696bf9c61a7a49dfc764c04dd33ef7a6b502a1e2"}, + {file = "cryptography-43.0.1-cp37-abi3-win_amd64.whl", hash = "sha256:666ae11966643886c2987b3b721899d250855718d6d9ce41b521252a17985f4d"}, + {file = "cryptography-43.0.1-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:ac119bb76b9faa00f48128b7f5679e1d8d437365c5d26f1c2c3f0da4ce1b553d"}, + {file = "cryptography-43.0.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1bbcce1a551e262dfbafb6e6252f1ae36a248e615ca44ba302df077a846a8806"}, + {file = "cryptography-43.0.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58d4e9129985185a06d849aa6df265bdd5a74ca6e1b736a77959b498e0505b85"}, + {file = "cryptography-43.0.1-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:d03a475165f3134f773d1388aeb19c2d25ba88b6a9733c5c590b9ff7bbfa2e0c"}, + {file = "cryptography-43.0.1-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:511f4273808ab590912a93ddb4e3914dfd8a388fed883361b02dea3791f292e1"}, + {file = "cryptography-43.0.1-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:80eda8b3e173f0f247f711eef62be51b599b5d425c429b5d4ca6a05e9e856baa"}, + {file = "cryptography-43.0.1-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:38926c50cff6f533f8a2dae3d7f19541432610d114a70808f0926d5aaa7121e4"}, + {file = "cryptography-43.0.1-cp39-abi3-win32.whl", hash = "sha256:a575913fb06e05e6b4b814d7f7468c2c660e8bb16d8d5a1faf9b33ccc569dd47"}, + {file = "cryptography-43.0.1-cp39-abi3-win_amd64.whl", hash = "sha256:d75601ad10b059ec832e78823b348bfa1a59f6b8d545db3a24fd44362a1564cb"}, + {file = "cryptography-43.0.1-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:ea25acb556320250756e53f9e20a4177515f012c9eaea17eb7587a8c4d8ae034"}, + {file = "cryptography-43.0.1-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:c1332724be35d23a854994ff0b66530119500b6053d0bd3363265f7e5e77288d"}, + {file = "cryptography-43.0.1-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:fba1007b3ef89946dbbb515aeeb41e30203b004f0b4b00e5e16078b518563289"}, + {file = "cryptography-43.0.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:5b43d1ea6b378b54a1dc99dd8a2b5be47658fe9a7ce0a58ff0b55f4b43ef2b84"}, + {file = "cryptography-43.0.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:88cce104c36870d70c49c7c8fd22885875d950d9ee6ab54df2745f83ba0dc365"}, + {file = "cryptography-43.0.1-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:9d3cdb25fa98afdd3d0892d132b8d7139e2c087da1712041f6b762e4f807cc96"}, + {file = "cryptography-43.0.1-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:e710bf40870f4db63c3d7d929aa9e09e4e7ee219e703f949ec4073b4294f6172"}, + {file = "cryptography-43.0.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7c05650fe8023c5ed0d46793d4b7d7e6cd9c04e68eabe5b0aeea836e37bdcec2"}, + {file = "cryptography-43.0.1.tar.gz", hash = "sha256:203e92a75716d8cfb491dc47c79e17d0d9207ccffcbcb35f598fbe463ae3444d"}, +] + +[package.dependencies] +cffi = {version = ">=1.12", markers = "platform_python_implementation != \"PyPy\""} + +[package.extras] +docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=1.1.1)"] +docstest = ["pyenchant (>=1.6.11)", "readme-renderer", "sphinxcontrib-spelling (>=4.0.1)"] +nox = ["nox"] +pep8test = ["check-sdist", "click", "mypy", "ruff"] +sdist = ["build"] +ssh = ["bcrypt (>=3.1.5)"] +test = ["certifi", "cryptography-vectors (==43.0.1)", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"] +test-randomorder = ["pytest-randomly"] + [[package]] name = "fastapi" version = "0.112.4" @@ -488,6 +656,27 @@ files = [ {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, ] +[[package]] +name = "paramiko" +version = "3.5.0" +description = "SSH2 protocol library" +optional = false +python-versions = ">=3.6" +files = [ + {file = "paramiko-3.5.0-py3-none-any.whl", hash = "sha256:1fedf06b085359051cd7d0d270cebe19e755a8a921cc2ddbfa647fb0cd7d68f9"}, + {file = "paramiko-3.5.0.tar.gz", hash = "sha256:ad11e540da4f55cedda52931f1a3f812a8238a7af7f62a60de538cd80bb28124"}, +] + +[package.dependencies] +bcrypt = ">=3.2" +cryptography = ">=3.3" +pynacl = ">=1.5" + +[package.extras] +all = ["gssapi (>=1.4.1)", "invoke (>=2.0)", "pyasn1 (>=0.1.7)", "pywin32 (>=2.1.8)"] +gssapi = ["gssapi (>=1.4.1)", "pyasn1 (>=0.1.7)", "pywin32 (>=2.1.8)"] +invoke = ["invoke (>=2.0)"] + [[package]] name = "pluggy" version = "1.5.0" @@ -532,6 +721,17 @@ files = [ [package.extras] test = ["enum34", "ipaddress", "mock", "pywin32", "wmi"] +[[package]] +name = "pycparser" +version = "2.22" +description = "C parser in Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"}, + {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"}, +] + [[package]] name = "pydantic" version = "2.9.0" @@ -676,6 +876,32 @@ azure-key-vault = ["azure-identity (>=1.16.0)", "azure-keyvault-secrets (>=4.8.0 toml = ["tomli (>=2.0.1)"] yaml = ["pyyaml (>=6.0.1)"] +[[package]] +name = "pynacl" +version = "1.5.0" +description = "Python binding to the Networking and Cryptography (NaCl) library" +optional = false +python-versions = ">=3.6" +files = [ + {file = "PyNaCl-1.5.0-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:401002a4aaa07c9414132aaed7f6836ff98f59277a234704ff66878c2ee4a0d1"}, + {file = "PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:52cb72a79269189d4e0dc537556f4740f7f0a9ec41c1322598799b0bdad4ef92"}, + {file = "PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a36d4a9dda1f19ce6e03c9a784a2921a4b726b02e1c736600ca9c22029474394"}, + {file = "PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:0c84947a22519e013607c9be43706dd42513f9e6ae5d39d3613ca1e142fba44d"}, + {file = "PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:06b8f6fa7f5de8d5d2f7573fe8c863c051225a27b61e6860fd047b1775807858"}, + {file = "PyNaCl-1.5.0-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:a422368fc821589c228f4c49438a368831cb5bbc0eab5ebe1d7fac9dded6567b"}, + {file = "PyNaCl-1.5.0-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:61f642bf2378713e2c2e1de73444a3778e5f0a38be6fee0fe532fe30060282ff"}, + {file = "PyNaCl-1.5.0-cp36-abi3-win32.whl", hash = "sha256:e46dae94e34b085175f8abb3b0aaa7da40767865ac82c928eeb9e57e1ea8a543"}, + {file = "PyNaCl-1.5.0-cp36-abi3-win_amd64.whl", hash = "sha256:20f42270d27e1b6a29f54032090b972d97f0a1b0948cc52392041ef7831fee93"}, + {file = "PyNaCl-1.5.0.tar.gz", hash = "sha256:8ac7448f09ab85811607bdd21ec2464495ac8b7c66d146bf545b0f08fb9220ba"}, +] + +[package.dependencies] +cffi = ">=1.4.1" + +[package.extras] +docs = ["sphinx (>=1.6.5)", "sphinx-rtd-theme"] +tests = ["hypothesis (>=3.27.0)", "pytest (>=3.2.1,!=3.3.0)"] + [[package]] name = "pytest" version = "8.3.2" @@ -1005,4 +1231,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = ">=3.12" -content-hash = "ba989d387012d96e5e342d3faa9a61b45de4c6fdb454ce81458b236a74ef1cfb" +content-hash = "8202838be049afccf73ed150282ad154fbf3ef544f1977d0f3e645e1938044c4" diff --git a/controller/pyproject.toml b/controller/pyproject.toml index e3c30b18..7de71ab5 100644 --- a/controller/pyproject.toml +++ b/controller/pyproject.toml @@ -24,6 +24,7 @@ pydantic-settings = "^2.4.0" alembic = "^1.13.2" sqlalchemy = "^2.0.32" python-multipart = "^0.0.9" +paramiko = "^3.5.0" [tool.poetry.group.enterprise] optional = true diff --git a/controller/thymis_controller/routers/api.py b/controller/thymis_controller/routers/api.py index 313f1dd9..fd90db5e 100644 --- a/controller/thymis_controller/routers/api.py +++ b/controller/thymis_controller/routers/api.py @@ -2,6 +2,7 @@ from fastapi import APIRouter, Depends, Request, WebSocket from fastapi.responses import FileResponse, RedirectResponse +from paramiko import SSHClient from thymis_controller import dependencies, models, modules, project from thymis_controller.dependencies import ( SessionAD, @@ -10,7 +11,12 @@ ) from thymis_controller.models.state import State from thymis_controller.routers import task -from thymis_controller.tcp_to_ws import tcp_to_websocket, websocket_to_tcp +from thymis_controller.tcp_to_ws import ( + channel_to_websocket, + tcp_to_websocket, + websocket_to_channel, + websocket_to_tcp, +) router = APIRouter( dependencies=[Depends(require_valid_user_session)], @@ -119,7 +125,7 @@ async def update( @router.websocket("/vnc/{identifier}") -async def websocket_endpoint( +async def vnc_websocket( identifier: str, websocket: WebSocket, state: State = Depends(dependencies.get_state), @@ -148,6 +154,42 @@ async def websocket_endpoint( ws_to_tcp_task.cancel() +@router.websocket("/terminal/{identifier}") +async def terminal_websocket( + identifier: str, + websocket: WebSocket, + state: State = Depends(dependencies.get_state), +): + device = next(device for device in state.devices if device.identifier == identifier) + + if device is None: + await websocket.close() + return + + await websocket.accept() + + tcp_ip = device.targetHost + tcp_port = 22 + + client = SSHClient() + client.load_system_host_keys() + client.connect(tcp_ip, tcp_port, "root") + + channel = client.invoke_shell() + + channel_to_ws_task = asyncio.create_task(channel_to_websocket(channel, websocket)) + ws_to_channel_task = asyncio.create_task(websocket_to_channel(channel, websocket)) + + try: + await asyncio.gather(channel_to_ws_task, ws_to_channel_task) + except Exception as e: + print(f"Error: {e}") + finally: + channel_to_ws_task.cancel() + ws_to_channel_task.cancel() + client.close() + + @router.get("/testSession") def test_session(session: SessionAD): session diff --git a/controller/thymis_controller/tcp_to_ws.py b/controller/thymis_controller/tcp_to_ws.py index afb7063a..405c437c 100644 --- a/controller/thymis_controller/tcp_to_ws.py +++ b/controller/thymis_controller/tcp_to_ws.py @@ -1,6 +1,7 @@ import asyncio from fastapi import WebSocket, WebSocketDisconnect +from paramiko import Channel async def tcp_to_websocket(tcp_reader, websocket): @@ -32,3 +33,38 @@ async def websocket_to_tcp(tcp_writer, websocket): print(f"Error in websocket_to_tcp: {e}") finally: tcp_writer.close() + + +async def channel_to_websocket(channel: Channel, websocket: WebSocket): + try: + while True: + if not channel.recv_ready(): + await asyncio.sleep(0.1) + continue + data = channel.recv(1024) + if not data: + break + await websocket.send_bytes(data) + except asyncio.CancelledError: + pass + except Exception as e: + print(f"Error in channel_to_websocket: {e}") + finally: + await websocket.close() + + +async def websocket_to_channel(channel: Channel, websocket: WebSocket): + try: + while True: + data = await websocket.receive_text() + if not data: + break + channel.send(data.encode()) + except WebSocketDisconnect: + pass + except asyncio.CancelledError: + pass + except Exception as e: + print(f"Error in websocket_to_channel: {e}") + finally: + channel.close() diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 7d93b466..c468eb02 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -8,6 +8,7 @@ "name": "thymis-frontend", "version": "0.0.1", "devDependencies": { + "@battlefieldduck/xterm-svelte": "^1.0.0", "@floating-ui/dom": "^1.6.10", "@novnc/novnc": "^1.5.0", "@playwright/test": "1.40.0", @@ -85,6 +86,29 @@ "node": ">=6.0.0" } }, + "node_modules/@battlefieldduck/xterm-svelte": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@battlefieldduck/xterm-svelte/-/xterm-svelte-1.0.0.tgz", + "integrity": "sha512-vrzFYTLUta169cVgdmGFLdjNzOcMPDXSd3lhh7kCT5r7peP52FHiM1QkmpLo61EfecFdc1VylbndemI79cBYJA==", + "dev": true, + "dependencies": { + "@xterm/addon-attach": "^0.11.0", + "@xterm/addon-canvas": "^0.7.0", + "@xterm/addon-clipboard": "^0.1.0", + "@xterm/addon-fit": "^0.10.0", + "@xterm/addon-image": "^0.8.0", + "@xterm/addon-ligatures": "^0.9.0", + "@xterm/addon-search": "^0.15.0", + "@xterm/addon-serialize": "^0.13.0", + "@xterm/addon-unicode11": "^0.8.0", + "@xterm/addon-web-links": "^0.11.0", + "@xterm/addon-webgl": "^0.18.0", + "@xterm/xterm": "^5.5.0" + }, + "peerDependencies": { + "svelte": "^4.0.0" + } + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.19.12", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz", @@ -1714,6 +1738,121 @@ "@types/estree": "^1.0.0" } }, + "node_modules/@xterm/addon-attach": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@xterm/addon-attach/-/addon-attach-0.11.0.tgz", + "integrity": "sha512-JboCN0QAY6ZLY/SSB/Zl2cQ5zW1Eh4X3fH7BnuR1NB7xGRhzbqU2Npmpiw/3zFlxDaU88vtKzok44JKi2L2V2Q==", + "dev": true, + "peerDependencies": { + "@xterm/xterm": "^5.0.0" + } + }, + "node_modules/@xterm/addon-canvas": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@xterm/addon-canvas/-/addon-canvas-0.7.0.tgz", + "integrity": "sha512-LF5LYcfvefJuJ7QotNRdRSPc9YASAVDeoT5uyXS/nZshZXjYplGXRECBGiznwvhNL2I8bq1Lf5MzRwstsYQ2Iw==", + "dev": true, + "peerDependencies": { + "@xterm/xterm": "^5.0.0" + } + }, + "node_modules/@xterm/addon-clipboard": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@xterm/addon-clipboard/-/addon-clipboard-0.1.0.tgz", + "integrity": "sha512-zdoM7p53T5sv/HbRTyp4hY0kKmEQ3MZvAvEtiXqNIHc/JdpqwByCtsTaQF5DX2n4hYdXRPO4P/eOS0QEhX1nPw==", + "dev": true, + "dependencies": { + "js-base64": "^3.7.5" + }, + "peerDependencies": { + "@xterm/xterm": "^5.4.0" + } + }, + "node_modules/@xterm/addon-fit": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@xterm/addon-fit/-/addon-fit-0.10.0.tgz", + "integrity": "sha512-UFYkDm4HUahf2lnEyHvio51TNGiLK66mqP2JoATy7hRZeXaGMRDr00JiSF7m63vR5WKATF605yEggJKsw0JpMQ==", + "dev": true, + "peerDependencies": { + "@xterm/xterm": "^5.0.0" + } + }, + "node_modules/@xterm/addon-image": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@xterm/addon-image/-/addon-image-0.8.0.tgz", + "integrity": "sha512-b/dqpFn3jUad2pUP5UpF4scPIh0WdxRQL/1qyiahGfUI85XZTCXo0py9G6AcOR2QYUw8eJ8EowGspT7BQcgw6A==", + "dev": true, + "peerDependencies": { + "@xterm/xterm": "^5.2.0" + } + }, + "node_modules/@xterm/addon-ligatures": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@xterm/addon-ligatures/-/addon-ligatures-0.9.0.tgz", + "integrity": "sha512-zVV1AHV1SIm/rdzR5VDPyg+qUnR1SjH4H75iXiB7r6YDa1yEHIqc/EwnUIwz+yeeZozkh8hjbH80L7luEgtxtQ==", + "dev": true, + "dependencies": { + "font-finder": "^1.1.0", + "font-ligatures": "^1.4.1" + }, + "engines": { + "node": ">8.0.0" + }, + "peerDependencies": { + "@xterm/xterm": "^5.0.0" + } + }, + "node_modules/@xterm/addon-search": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@xterm/addon-search/-/addon-search-0.15.0.tgz", + "integrity": "sha512-ZBZKLQ+EuKE83CqCmSSz5y1tx+aNOCUaA7dm6emgOX+8J9H1FWXZyrKfzjwzV+V14TV3xToz1goIeRhXBS5qjg==", + "dev": true, + "peerDependencies": { + "@xterm/xterm": "^5.0.0" + } + }, + "node_modules/@xterm/addon-serialize": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@xterm/addon-serialize/-/addon-serialize-0.13.0.tgz", + "integrity": "sha512-kGs8o6LWAmN1l2NpMp01/YkpxbmO4UrfWybeGu79Khw5K9+Krp7XhXbBTOTc3GJRRhd6EmILjpR8k5+odY39YQ==", + "dev": true, + "peerDependencies": { + "@xterm/xterm": "^5.0.0" + } + }, + "node_modules/@xterm/addon-unicode11": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@xterm/addon-unicode11/-/addon-unicode11-0.8.0.tgz", + "integrity": "sha512-LxinXu8SC4OmVa6FhgwsVCBZbr8WoSGzBl2+vqe8WcQ6hb1r6Gj9P99qTNdPiFPh4Ceiu2pC8xukZ6+2nnh49Q==", + "dev": true, + "peerDependencies": { + "@xterm/xterm": "^5.0.0" + } + }, + "node_modules/@xterm/addon-web-links": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@xterm/addon-web-links/-/addon-web-links-0.11.0.tgz", + "integrity": "sha512-nIHQ38pQI+a5kXnRaTgwqSHnX7KE6+4SVoceompgHL26unAxdfP6IPqUTSYPQgSwM56hsElfoNrrW5V7BUED/Q==", + "dev": true, + "peerDependencies": { + "@xterm/xterm": "^5.0.0" + } + }, + "node_modules/@xterm/addon-webgl": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/@xterm/addon-webgl/-/addon-webgl-0.18.0.tgz", + "integrity": "sha512-xCnfMBTI+/HKPdRnSOHaJDRqEpq2Ugy8LEj9GiY4J3zJObo3joylIFaMvzBwbYRg8zLtkO0KQaStCeSfoaI2/w==", + "dev": true, + "peerDependencies": { + "@xterm/xterm": "^5.0.0" + } + }, + "node_modules/@xterm/xterm": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@xterm/xterm/-/xterm-5.5.0.tgz", + "integrity": "sha512-hqJHYaQb5OptNunnyAnkHyM8aCjZ1MEIDTQu1iIbbTD/xops91NB5yq1ZK/dC2JDbVWtF23zUtl9JE2NqwT87A==", + "dev": true + }, "node_modules/@yr/monotone-cubic-spline": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@yr/monotone-cubic-spline/-/monotone-cubic-spline-1.0.3.tgz", @@ -3090,6 +3229,45 @@ "tailwindcss": "^3.3.2" } }, + "node_modules/font-finder": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/font-finder/-/font-finder-1.1.0.tgz", + "integrity": "sha512-wpCL2uIbi6GurJbU7ZlQ3nGd61Ho+dSU6U83/xJT5UPFfN35EeCW/rOtS+5k+IuEZu2SYmHzDIPL9eA5tSYRAw==", + "dev": true, + "dependencies": { + "get-system-fonts": "^2.0.0", + "promise-stream-reader": "^1.0.1" + }, + "engines": { + "node": ">8.0.0" + } + }, + "node_modules/font-ligatures": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/font-ligatures/-/font-ligatures-1.4.1.tgz", + "integrity": "sha512-7W6zlfyhvCqShZ5ReUWqmSd9vBaUudW0Hxis+tqUjtHhsPU+L3Grf8mcZAtCiXHTzorhwdRTId2WeH/88gdFkw==", + "dev": true, + "dependencies": { + "font-finder": "^1.0.3", + "lru-cache": "^6.0.0", + "opentype.js": "^0.8.0" + }, + "engines": { + "node": ">8.0.0" + } + }, + "node_modules/font-ligatures/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/foreground-child": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", @@ -3169,6 +3347,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/get-system-fonts": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-system-fonts/-/get-system-fonts-2.0.2.tgz", + "integrity": "sha512-zzlgaYnHMIEgHRrfC7x0Qp0Ylhw/sHpM6MHXeVBTYIsvGf5GpbnClB+Q6rAPdn+0gd2oZZIo6Tj3EaWrt4VhDQ==", + "dev": true, + "engines": { + "node": ">8.0.0" + } + }, "node_modules/glob": { "version": "10.4.5", "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", @@ -3509,6 +3696,12 @@ "jiti": "bin/jiti.js" } }, + "node_modules/js-base64": { + "version": "3.7.7", + "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-3.7.7.tgz", + "integrity": "sha512-7rCnleh0z2CkXhH67J8K1Ytz0b2Y+yxTPL+/KOJoa20hfnVQ/3/T6W/KflYI4bRHRagNeXeU2bkNGI3v1oS/lw==", + "dev": true + }, "node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -3973,6 +4166,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/opentype.js": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/opentype.js/-/opentype.js-0.8.0.tgz", + "integrity": "sha512-FQHR4oGP+a0m/f6yHoRpBOIbn/5ZWxKd4D/djHVJu8+KpBTYrJda0b7mLcgDEMWXE9xBCJm+qb0yv6FcvPjukg==", + "dev": true, + "dependencies": { + "tiny-inflate": "^1.0.2" + }, + "bin": { + "ot": "bin/ot" + } + }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -4472,6 +4677,15 @@ "svelte": "^3.2.0 || ^4.0.0-next.0 || ^5.0.0-next.0" } }, + "node_modules/promise-stream-reader": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-stream-reader/-/promise-stream-reader-1.0.1.tgz", + "integrity": "sha512-Tnxit5trUjBAqqZCGWwjyxhmgMN4hGrtpW3Oc/tRI4bpm/O2+ej72BB08l6JBnGQgVDGCLvHFGjGgQS6vzhwXg==", + "dev": true, + "engines": { + "node": ">8.0.0" + } + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -5574,6 +5788,12 @@ "globrex": "^0.1.2" } }, + "node_modules/tiny-inflate": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz", + "integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==", + "dev": true + }, "node_modules/tinybench": { "version": "2.9.0", "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", @@ -6504,6 +6724,12 @@ "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, "node_modules/yaml": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.5.0.tgz", diff --git a/frontend/package.json b/frontend/package.json index 16dde995..9a8d4a2f 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -15,6 +15,7 @@ "test:unit": "vitest" }, "devDependencies": { + "@battlefieldduck/xterm-svelte": "^1.0.0", "@floating-ui/dom": "^1.6.10", "@novnc/novnc": "^1.5.0", "@playwright/test": "1.40.0", diff --git a/frontend/src/routes/(authenticated)/terminal/+page.svelte b/frontend/src/routes/(authenticated)/terminal/+page.svelte index e69de29b..3af64063 100644 --- a/frontend/src/routes/(authenticated)/terminal/+page.svelte +++ b/frontend/src/routes/(authenticated)/terminal/+page.svelte @@ -0,0 +1,37 @@ + + + From b0b29c62284c7004d8016c37bd47a6018a83a931 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20Schm=C3=B6cker?= Date: Wed, 2 Oct 2024 14:56:08 +0200 Subject: [PATCH 02/13] feat: better error messages for non-connecting terminal --- controller/thymis_controller/routers/api.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/controller/thymis_controller/routers/api.py b/controller/thymis_controller/routers/api.py index fd90db5e..8a7bd2ea 100644 --- a/controller/thymis_controller/routers/api.py +++ b/controller/thymis_controller/routers/api.py @@ -173,9 +173,15 @@ async def terminal_websocket( client = SSHClient() client.load_system_host_keys() - client.connect(tcp_ip, tcp_port, "root") - channel = client.invoke_shell() + try: + client.connect(tcp_ip, tcp_port, "root") + channel = client.invoke_shell() + except Exception as e: + await websocket.send_bytes(str(e).encode()) + await websocket.close() + client.close() + return channel_to_ws_task = asyncio.create_task(channel_to_websocket(channel, websocket)) ws_to_channel_task = asyncio.create_task(websocket_to_channel(channel, websocket)) From 363128bf8472f19f152cc0b9ebc0aadc3431241c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20Schm=C3=B6cker?= Date: Wed, 2 Oct 2024 14:57:53 +0200 Subject: [PATCH 03/13] fix: better attempt to close terminal connection --- controller/thymis_controller/tcp_to_ws.py | 2 ++ .../(authenticated)/terminal/+page.svelte | 24 ++++++++++++------- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/controller/thymis_controller/tcp_to_ws.py b/controller/thymis_controller/tcp_to_ws.py index 405c437c..dc81f176 100644 --- a/controller/thymis_controller/tcp_to_ws.py +++ b/controller/thymis_controller/tcp_to_ws.py @@ -38,6 +38,8 @@ async def websocket_to_tcp(tcp_writer, websocket): async def channel_to_websocket(channel: Channel, websocket: WebSocket): try: while True: + if channel.closed: + break if not channel.recv_ready(): await asyncio.sleep(0.1) continue diff --git a/frontend/src/routes/(authenticated)/terminal/+page.svelte b/frontend/src/routes/(authenticated)/terminal/+page.svelte index 3af64063..de1703e0 100644 --- a/frontend/src/routes/(authenticated)/terminal/+page.svelte +++ b/frontend/src/routes/(authenticated)/terminal/+page.svelte @@ -5,32 +5,40 @@ ITerminalInitOnlyOptions, Terminal } from '@battlefieldduck/xterm-svelte'; - import { globalNavSelectedDevice } from '$lib/state'; + import { globalNavSelectedDevice, type Device } from '$lib/state'; $: device = $globalNavSelectedDevice; let terminal: Terminal; + let ws: WebSocket; const options: ITerminalOptions & ITerminalInitOnlyOptions = { - cursorBlink: true, - convertEol: true + cursorBlink: true }; async function onLoad(event: CustomEvent<{ terminal: Terminal }>) { - if (!device) { - return; - } - terminal = event.detail.terminal; + } + + const initTerminal = async (device: Device, terminal: Terminal) => { const scheme = window.location.protocol === 'https:' ? 'wss' : 'ws'; const url = `${scheme}://${window.location.host}/api/terminal/${device.identifier}`; - const ws = new WebSocket(url); + ws = new WebSocket(url); const fitAddon = new (await XtermAddon.FitAddon()).FitAddon(); const attachAddon = new (await XtermAddon.AttachAddon()).AttachAddon(ws); terminal.loadAddon(fitAddon); terminal.loadAddon(attachAddon); fitAddon.fit(); + }; + + $: { + terminal?.reset(); + ws?.close(); + + if (device && terminal) { + initTerminal(device, terminal); + } } From 76b878da732610ed6f79c573ce9574ab9f02537d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20Schm=C3=B6cker?= Date: Wed, 2 Oct 2024 15:19:54 +0200 Subject: [PATCH 04/13] feat: improve terminal sizing --- controller/thymis_controller/tcp_to_ws.py | 2 +- frontend/src/routes/(authenticated)/terminal/+page.svelte | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/controller/thymis_controller/tcp_to_ws.py b/controller/thymis_controller/tcp_to_ws.py index dc81f176..5bc0f6cc 100644 --- a/controller/thymis_controller/tcp_to_ws.py +++ b/controller/thymis_controller/tcp_to_ws.py @@ -41,7 +41,7 @@ async def channel_to_websocket(channel: Channel, websocket: WebSocket): if channel.closed: break if not channel.recv_ready(): - await asyncio.sleep(0.1) + await asyncio.sleep(0.01) continue data = channel.recv(1024) if not data: diff --git a/frontend/src/routes/(authenticated)/terminal/+page.svelte b/frontend/src/routes/(authenticated)/terminal/+page.svelte index de1703e0..cce6613c 100644 --- a/frontend/src/routes/(authenticated)/terminal/+page.svelte +++ b/frontend/src/routes/(authenticated)/terminal/+page.svelte @@ -26,10 +26,12 @@ ws = new WebSocket(url); const fitAddon = new (await XtermAddon.FitAddon()).FitAddon(); - const attachAddon = new (await XtermAddon.AttachAddon()).AttachAddon(ws); terminal.loadAddon(fitAddon); - terminal.loadAddon(attachAddon); fitAddon.fit(); + console.log(fitAddon.proposeDimensions()); + + const attachAddon = new (await XtermAddon.AttachAddon()).AttachAddon(ws); + terminal.loadAddon(attachAddon); }; $: { @@ -42,4 +44,4 @@ } - + From f40ddd0fb9e305bf3cc681e19d707f2c2b7c174a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20Schm=C3=B6cker?= Date: Wed, 2 Oct 2024 15:55:58 +0200 Subject: [PATCH 05/13] fix: npmDepsHash --- frontend/default.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/default.nix b/frontend/default.nix index 6845eaba..b7740757 100644 --- a/frontend/default.nix +++ b/frontend/default.nix @@ -7,7 +7,7 @@ buildNpmPackage { pname = "thymis-frontend"; version = "0.0.1"; src = ./.; - npmDepsHash = "sha256-Fw+j3dqqH5zWGuZScJA+8GmCAGqsgbyGK3hU/XNY8Kc="; + npmDepsHash = "sha256-xu0kr0YS/7iYJKrHefKPrTAHBK36k4VGgvqCnSV36cE="; postInstall = '' mkdir -p $packageOut/build cp -r ./build/* $packageOut/build From 1dcd28f06d73f481eadcbf71f6510c39d5008e2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20Schm=C3=B6cker?= Date: Thu, 3 Oct 2024 12:31:53 +0200 Subject: [PATCH 06/13] fix: use bcrypt from nixpkgs --- controller/default.nix | 1 + 1 file changed, 1 insertion(+) diff --git a/controller/default.nix b/controller/default.nix index 4d6e68d9..f81e3f51 100644 --- a/controller/default.nix +++ b/controller/default.nix @@ -14,6 +14,7 @@ let preferWheels = true; overrides = poetry2nix.overrides.withDefaults (self: super: { thymis-enterprise = null; + bcrypt = python312.pkgs.bcrypt; }); groups = [ ]; checkGroups = [ "test" ]; From a70f3fb005eeaa577860ebb7408573361cdf1782 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20Schm=C3=B6cker?= Date: Thu, 3 Oct 2024 13:12:36 +0200 Subject: [PATCH 07/13] fix: improve reconnecting to terminal in frontend --- .../(authenticated)/terminal/+page.svelte | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/frontend/src/routes/(authenticated)/terminal/+page.svelte b/frontend/src/routes/(authenticated)/terminal/+page.svelte index cce6613c..4ecc3f51 100644 --- a/frontend/src/routes/(authenticated)/terminal/+page.svelte +++ b/frontend/src/routes/(authenticated)/terminal/+page.svelte @@ -6,6 +6,7 @@ Terminal } from '@battlefieldduck/xterm-svelte'; import { globalNavSelectedDevice, type Device } from '$lib/state'; + import { onDestroy, onMount } from 'svelte'; $: device = $globalNavSelectedDevice; @@ -32,11 +33,29 @@ const attachAddon = new (await XtermAddon.AttachAddon()).AttachAddon(ws); terminal.loadAddon(attachAddon); + + terminal.writeln(`Connecting to ${device.displayName}...`); }; - $: { + const resetConnection = () => { terminal?.reset(); ws?.close(); + }; + + onMount(() => { + resetConnection(); + + if (device && terminal) { + initTerminal(device, terminal); + } + }); + + onDestroy(() => { + resetConnection(); + }); + + $: { + resetConnection(); if (device && terminal) { initTerminal(device, terminal); From ed76925b52880d970995b70bac0f040e2d7ed775 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20Schm=C3=B6cker?= Date: Thu, 3 Oct 2024 18:15:07 +0200 Subject: [PATCH 08/13] fix: simplefied load --- .../routes/(authenticated)/terminal/+page.svelte | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/frontend/src/routes/(authenticated)/terminal/+page.svelte b/frontend/src/routes/(authenticated)/terminal/+page.svelte index 4ecc3f51..303c4746 100644 --- a/frontend/src/routes/(authenticated)/terminal/+page.svelte +++ b/frontend/src/routes/(authenticated)/terminal/+page.svelte @@ -17,9 +17,9 @@ cursorBlink: true }; - async function onLoad(event: CustomEvent<{ terminal: Terminal }>) { + const onLoad = (event: CustomEvent<{ terminal: Terminal }>) => { terminal = event.detail.terminal; - } + }; const initTerminal = async (device: Device, terminal: Terminal) => { const scheme = window.location.protocol === 'https:' ? 'wss' : 'ws'; @@ -29,7 +29,6 @@ const fitAddon = new (await XtermAddon.FitAddon()).FitAddon(); terminal.loadAddon(fitAddon); fitAddon.fit(); - console.log(fitAddon.proposeDimensions()); const attachAddon = new (await XtermAddon.AttachAddon()).AttachAddon(ws); terminal.loadAddon(attachAddon); @@ -42,14 +41,6 @@ ws?.close(); }; - onMount(() => { - resetConnection(); - - if (device && terminal) { - initTerminal(device, terminal); - } - }); - onDestroy(() => { resetConnection(); }); From 2aa3f6566283797c616b5dbd8d9df2b6581b2c9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20Schm=C3=B6cker?= Date: Thu, 3 Oct 2024 18:23:46 +0200 Subject: [PATCH 09/13] fix: letter spacing --- frontend/src/routes/(authenticated)/terminal/+page.svelte | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frontend/src/routes/(authenticated)/terminal/+page.svelte b/frontend/src/routes/(authenticated)/terminal/+page.svelte index 303c4746..1e67800e 100644 --- a/frontend/src/routes/(authenticated)/terminal/+page.svelte +++ b/frontend/src/routes/(authenticated)/terminal/+page.svelte @@ -14,7 +14,8 @@ let ws: WebSocket; const options: ITerminalOptions & ITerminalInitOnlyOptions = { - cursorBlink: true + cursorBlink: true, + letterSpacing: undefined }; const onLoad = (event: CustomEvent<{ terminal: Terminal }>) => { From 948c7ba0f13b2f083cf6e8fded701b3ceee3c6c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20Schm=C3=B6cker?= Date: Thu, 3 Oct 2024 18:47:18 +0200 Subject: [PATCH 10/13] fix: fit terminal on resize --- frontend/src/routes/(authenticated)/terminal/+page.svelte | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/frontend/src/routes/(authenticated)/terminal/+page.svelte b/frontend/src/routes/(authenticated)/terminal/+page.svelte index 1e67800e..248db7b1 100644 --- a/frontend/src/routes/(authenticated)/terminal/+page.svelte +++ b/frontend/src/routes/(authenticated)/terminal/+page.svelte @@ -31,6 +31,11 @@ terminal.loadAddon(fitAddon); fitAddon.fit(); + const observer = new ResizeObserver(() => { + fitAddon?.fit(); + }); + if (terminal.element) observer.observe(terminal.element); + const attachAddon = new (await XtermAddon.AttachAddon()).AttachAddon(ws); terminal.loadAddon(attachAddon); From bb81002c6aea124e5980937d456cd54e96acf49d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20Schm=C3=B6cker?= Date: Thu, 3 Oct 2024 19:01:20 +0200 Subject: [PATCH 11/13] fix: letter spacing --- frontend/src/routes/(authenticated)/terminal/+page.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/routes/(authenticated)/terminal/+page.svelte b/frontend/src/routes/(authenticated)/terminal/+page.svelte index 248db7b1..7ad0094f 100644 --- a/frontend/src/routes/(authenticated)/terminal/+page.svelte +++ b/frontend/src/routes/(authenticated)/terminal/+page.svelte @@ -15,7 +15,7 @@ const options: ITerminalOptions & ITerminalInitOnlyOptions = { cursorBlink: true, - letterSpacing: undefined + letterSpacing: 0 }; const onLoad = (event: CustomEvent<{ terminal: Terminal }>) => { From 0f815e5c2dee198bb7b4e3a0e02f79e192dc627a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20Schm=C3=B6cker?= Date: Thu, 3 Oct 2024 19:05:05 +0200 Subject: [PATCH 12/13] feat: add webgl addon --- frontend/src/routes/(authenticated)/terminal/+page.svelte | 3 +++ 1 file changed, 3 insertions(+) diff --git a/frontend/src/routes/(authenticated)/terminal/+page.svelte b/frontend/src/routes/(authenticated)/terminal/+page.svelte index 7ad0094f..3f67892e 100644 --- a/frontend/src/routes/(authenticated)/terminal/+page.svelte +++ b/frontend/src/routes/(authenticated)/terminal/+page.svelte @@ -39,6 +39,9 @@ const attachAddon = new (await XtermAddon.AttachAddon()).AttachAddon(ws); terminal.loadAddon(attachAddon); + const webglAddon = new (await XtermAddon.WebglAddon()).WebglAddon(); + terminal.loadAddon(webglAddon); + terminal.writeln(`Connecting to ${device.displayName}...`); }; From 2bdbda255e055729fea52084064647ca9f2af94c Mon Sep 17 00:00:00 2001 From: Eli Kogan-Wang Date: Thu, 3 Oct 2024 19:16:21 +0200 Subject: [PATCH 13/13] handle pty resize --- controller/thymis_controller/tcp_to_ws.py | 8 +++++++- frontend/src/routes/(authenticated)/terminal/+page.svelte | 2 ++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/controller/thymis_controller/tcp_to_ws.py b/controller/thymis_controller/tcp_to_ws.py index 5bc0f6cc..1ea108c4 100644 --- a/controller/thymis_controller/tcp_to_ws.py +++ b/controller/thymis_controller/tcp_to_ws.py @@ -1,4 +1,5 @@ import asyncio +import json from fastapi import WebSocket, WebSocketDisconnect from paramiko import Channel @@ -61,7 +62,12 @@ async def websocket_to_channel(channel: Channel, websocket: WebSocket): data = await websocket.receive_text() if not data: break - channel.send(data.encode()) + if data.startswith("\x04"): + data = data[1:] + data = json.loads(data) + channel.resize_pty(width=data["cols"], height=data["rows"]) + else: + channel.send(data.encode()) except WebSocketDisconnect: pass except asyncio.CancelledError: diff --git a/frontend/src/routes/(authenticated)/terminal/+page.svelte b/frontend/src/routes/(authenticated)/terminal/+page.svelte index 3f67892e..d0f0565c 100644 --- a/frontend/src/routes/(authenticated)/terminal/+page.svelte +++ b/frontend/src/routes/(authenticated)/terminal/+page.svelte @@ -33,6 +33,8 @@ const observer = new ResizeObserver(() => { fitAddon?.fit(); + const dims = fitAddon.proposeDimensions(); + ws.send(`\x04${JSON.stringify(dims)}`); }); if (terminal.element) observer.observe(terminal.element);