From 0b1a19164a8defaf8b0d92ae6ca225f4c8dd12da Mon Sep 17 00:00:00 2001 From: Jack Kingsman Date: Sun, 15 Mar 2026 15:47:09 -0700 Subject: [PATCH] Fix typo and add better ops for decrypt API calls --- app/routers/packets.py | 70 +++++------------- frontend/AGENTS.md | 4 + ...-CBFX17Kn.js => BotCodeEditor-DUWnXYXA.js} | Bin 467083 -> 467083 bytes ...n.js.map => BotCodeEditor-DUWnXYXA.js.map} | Bin 1956915 -> 1956915 bytes ...l-RTfofPO9.js => CrackerPanel-CqHBN9pz.js} | Bin 129410 -> 129410 bytes ...O9.js.map => CrackerPanel-CqHBN9pz.js.map} | Bin 396562 -> 396562 bytes ...apView-DG71Do9A.js => MapView-CgY0ZIub.js} | Bin 4086 -> 4086 bytes ...G71Do9A.js.map => MapView-CgY0ZIub.js.map} | Bin 14879 -> 14879 bytes ...bvxuvI.js => NeighborsMiniMap-nhWnbDjS.js} | Bin 1670 -> 1670 bytes ...s.map => NeighborsMiniMap-nhWnbDjS.js.map} | Bin 5886 -> 5886 bytes ...p-DiGw6Qka.js => PathRouteMap-BgANOQRu.js} | Bin 3868 -> 3868 bytes ...ka.js.map => PathRouteMap-BgANOQRu.js.map} | Bin 14070 -> 14070 bytes .../{Popup-JtfOLvZE.js => Popup-65Kx45OI.js} | Bin 920 -> 920 bytes ...-JtfOLvZE.js.map => Popup-65Kx45OI.js.map} | Bin 4181 -> 4181 bytes ...8zxo_.js => RepeaterDashboard-B86OTQtd.js} | Bin 28491 -> 28491 bytes ....map => RepeaterDashboard-B86OTQtd.js.map} | Bin 89840 -> 89840 bytes ...iew-Cs7v3m6K.js => SearchView-BFMnGDyJ.js} | Bin 5106 -> 5106 bytes ...3m6K.js.map => SearchView-BFMnGDyJ.js.map} | Bin 16886 -> 16886 bytes ...-C-alEUMR.js => SettingsModal-CmnPdiWo.js} | Bin 77263 -> 77263 bytes ...R.js.map => SettingsModal-CmnPdiWo.js.map} | Bin 196690 -> 196690 bytes ...BBRdi0S-.js => VisualizerView-BX96-zH4.js} | Bin 590321 -> 590321 bytes ....js.map => VisualizerView-BX96-zH4.js.map} | Bin 3112795 -> 3112795 bytes frontend/prebuilt/assets/hooks-BHXvHkvm.js | Bin 0 -> 157 bytes ...-NBTgSuxX.js.map => hooks-BHXvHkvm.js.map} | Bin 1128 -> 1128 bytes frontend/prebuilt/assets/hooks-NBTgSuxX.js | Bin 157 -> 0 bytes .../{index-BJ3L5G2s.js => index-D8to1fXL.js} | Bin 582484 -> 582670 bytes ...-BJ3L5G2s.js.map => index-D8to1fXL.js.map} | Bin 2111065 -> 2111501 bytes ...eaflet-TNk2YzRY.js => leaflet-CQVZ2keI.js} | Bin 153764 -> 153764 bytes ...Nk2YzRY.js.map => leaflet-CQVZ2keI.js.map} | Bin 700833 -> 700833 bytes ...ator-ChUIbJIJ.js => separator-CqKVy-fj.js} | Bin 1068 -> 1068 bytes ...IbJIJ.js.map => separator-CqKVy-fj.js.map} | Bin 5058 -> 5058 bytes frontend/prebuilt/index.html | Bin 2761 -> 2761 bytes frontend/src/components/ChatHeader.tsx | 2 +- frontend/src/components/NewMessageModal.tsx | 7 ++ frontend/src/test/newMessageModal.test.tsx | 24 ++++++ tests/test_packets_router.py | 51 ++++++------- 36 files changed, 75 insertions(+), 83 deletions(-) rename frontend/prebuilt/assets/{BotCodeEditor-CBFX17Kn.js => BotCodeEditor-DUWnXYXA.js} (99%) rename frontend/prebuilt/assets/{BotCodeEditor-CBFX17Kn.js.map => BotCodeEditor-DUWnXYXA.js.map} (99%) rename frontend/prebuilt/assets/{CrackerPanel-RTfofPO9.js => CrackerPanel-CqHBN9pz.js} (99%) rename frontend/prebuilt/assets/{CrackerPanel-RTfofPO9.js.map => CrackerPanel-CqHBN9pz.js.map} (99%) rename frontend/prebuilt/assets/{MapView-DG71Do9A.js => MapView-CgY0ZIub.js} (92%) rename frontend/prebuilt/assets/{MapView-DG71Do9A.js.map => MapView-CgY0ZIub.js.map} (99%) rename frontend/prebuilt/assets/{NeighborsMiniMap-D6bvxuvI.js => NeighborsMiniMap-nhWnbDjS.js} (85%) rename frontend/prebuilt/assets/{NeighborsMiniMap-D6bvxuvI.js.map => NeighborsMiniMap-nhWnbDjS.js.map} (97%) rename frontend/prebuilt/assets/{PathRouteMap-DiGw6Qka.js => PathRouteMap-BgANOQRu.js} (95%) rename frontend/prebuilt/assets/{PathRouteMap-DiGw6Qka.js.map => PathRouteMap-BgANOQRu.js.map} (99%) rename frontend/prebuilt/assets/{Popup-JtfOLvZE.js => Popup-65Kx45OI.js} (81%) rename frontend/prebuilt/assets/{Popup-JtfOLvZE.js.map => Popup-65Kx45OI.js.map} (97%) rename frontend/prebuilt/assets/{RepeaterDashboard-bua8zxo_.js => RepeaterDashboard-B86OTQtd.js} (98%) rename frontend/prebuilt/assets/{RepeaterDashboard-bua8zxo_.js.map => RepeaterDashboard-B86OTQtd.js.map} (99%) rename frontend/prebuilt/assets/{SearchView-Cs7v3m6K.js => SearchView-BFMnGDyJ.js} (97%) rename frontend/prebuilt/assets/{SearchView-Cs7v3m6K.js.map => SearchView-BFMnGDyJ.js.map} (99%) rename frontend/prebuilt/assets/{SettingsModal-C-alEUMR.js => SettingsModal-CmnPdiWo.js} (99%) rename frontend/prebuilt/assets/{SettingsModal-C-alEUMR.js.map => SettingsModal-CmnPdiWo.js.map} (99%) rename frontend/prebuilt/assets/{VisualizerView-BBRdi0S-.js => VisualizerView-BX96-zH4.js} (99%) rename frontend/prebuilt/assets/{VisualizerView-BBRdi0S-.js.map => VisualizerView-BX96-zH4.js.map} (99%) create mode 100644 frontend/prebuilt/assets/hooks-BHXvHkvm.js rename frontend/prebuilt/assets/{hooks-NBTgSuxX.js.map => hooks-BHXvHkvm.js.map} (90%) delete mode 100644 frontend/prebuilt/assets/hooks-NBTgSuxX.js rename frontend/prebuilt/assets/{index-BJ3L5G2s.js => index-D8to1fXL.js} (90%) rename frontend/prebuilt/assets/{index-BJ3L5G2s.js.map => index-D8to1fXL.js.map} (95%) rename frontend/prebuilt/assets/{leaflet-TNk2YzRY.js => leaflet-CQVZ2keI.js} (99%) rename frontend/prebuilt/assets/{leaflet-TNk2YzRY.js.map => leaflet-CQVZ2keI.js.map} (99%) rename frontend/prebuilt/assets/{separator-ChUIbJIJ.js => separator-CqKVy-fj.js} (89%) rename frontend/prebuilt/assets/{separator-ChUIbJIJ.js.map => separator-CqKVy-fj.js.map} (98%) diff --git a/app/routers/packets.py b/app/routers/packets.py index 8142e679..00316ee5 100644 --- a/app/routers/packets.py +++ b/app/routers/packets.py @@ -3,7 +3,7 @@ from hashlib import sha256 from sqlite3 import OperationalError import aiosqlite -from fastapi import APIRouter, BackgroundTasks +from fastapi import APIRouter, BackgroundTasks, HTTPException, Response, status from pydantic import BaseModel, Field from app.database import db @@ -40,6 +40,10 @@ class DecryptResult(BaseModel): message: str +def _bad_request(detail: str) -> HTTPException: + return HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=detail) + + async def _run_historical_channel_decryption( channel_key_bytes: bytes, channel_key_hex: str, display_name: str | None = None ) -> None: @@ -100,7 +104,7 @@ async def get_undecrypted_count() -> dict: @router.post("/decrypt/historical", response_model=DecryptResult) async def decrypt_historical_packets( - request: DecryptRequest, background_tasks: BackgroundTasks + request: DecryptRequest, background_tasks: BackgroundTasks, response: Response ) -> DecryptResult: """ Attempt to decrypt historical packets with the provided key. @@ -112,27 +116,15 @@ async def decrypt_historical_packets( try: channel_key_bytes = bytes.fromhex(request.channel_key) if len(channel_key_bytes) != 16: - return DecryptResult( - started=False, - total_packets=0, - message="Channel key must be 16 bytes (32 hex chars)", - ) + raise _bad_request("Channel key must be 16 bytes (32 hex chars)") channel_key_hex = request.channel_key.upper() except ValueError: - return DecryptResult( - started=False, - total_packets=0, - message="Invalid hex string for channel key", - ) + raise _bad_request("Invalid hex string for channel key") from None elif request.channel_name: channel_key_bytes = sha256(request.channel_name.encode("utf-8")).digest()[:16] channel_key_hex = channel_key_bytes.hex().upper() else: - return DecryptResult( - started=False, - total_packets=0, - message="Must provide channel_key or channel_name", - ) + raise _bad_request("Must provide channel_key or channel_name") # Get count and lookup channel name for display count = await RawPacketRepository.get_undecrypted_count() @@ -148,6 +140,7 @@ async def decrypt_historical_packets( background_tasks.add_task( _run_historical_channel_decryption, channel_key_bytes, channel_key_hex, display_name ) + response.status_code = status.HTTP_202_ACCEPTED return DecryptResult( started=True, @@ -158,48 +151,24 @@ async def decrypt_historical_packets( elif request.key_type == "contact": # DM decryption if not request.private_key: - return DecryptResult( - started=False, - total_packets=0, - message="Must provide private_key for contact decryption", - ) + raise _bad_request("Must provide private_key for contact decryption") if not request.contact_public_key: - return DecryptResult( - started=False, - total_packets=0, - message="Must provide contact_public_key for contact decryption", - ) + raise _bad_request("Must provide contact_public_key for contact decryption") try: private_key_bytes = bytes.fromhex(request.private_key) if len(private_key_bytes) != 64: - return DecryptResult( - started=False, - total_packets=0, - message="Private key must be 64 bytes (128 hex chars)", - ) + raise _bad_request("Private key must be 64 bytes (128 hex chars)") except ValueError: - return DecryptResult( - started=False, - total_packets=0, - message="Invalid hex string for private key", - ) + raise _bad_request("Invalid hex string for private key") from None try: contact_public_key_bytes = bytes.fromhex(request.contact_public_key) if len(contact_public_key_bytes) != 32: - return DecryptResult( - started=False, - total_packets=0, - message="Contact public key must be 32 bytes (64 hex chars)", - ) + raise _bad_request("Contact public key must be 32 bytes (64 hex chars)") contact_public_key_hex = request.contact_public_key.lower() except ValueError: - return DecryptResult( - started=False, - total_packets=0, - message="Invalid hex string for contact public key", - ) + raise _bad_request("Invalid hex string for contact public key") from None packets = await RawPacketRepository.get_undecrypted_text_messages() count = len(packets) @@ -223,6 +192,7 @@ async def decrypt_historical_packets( contact_public_key_hex, display_name, ) + response.status_code = status.HTTP_202_ACCEPTED return DecryptResult( started=True, @@ -230,11 +200,7 @@ async def decrypt_historical_packets( message=f"Started DM decryption of {count} TEXT_MESSAGE packets in background", ) - return DecryptResult( - started=False, - total_packets=0, - message="key_type must be 'channel' or 'contact'", - ) + raise _bad_request("key_type must be 'channel' or 'contact'") class MaintenanceRequest(BaseModel): diff --git a/frontend/AGENTS.md b/frontend/AGENTS.md index f4fac06c..02bf7899 100644 --- a/frontend/AGENTS.md +++ b/frontend/AGENTS.md @@ -415,6 +415,10 @@ PYTHONPATH=. uv run pytest tests/ -v ## Errata & Known Non-Issues +### Contacts rollup uses mention styling for unread DMs + +This is intentional. In the sidebar section headers, unread direct messages are treated as mention-equivalent, so the Contacts rollup uses the highlighted mention-style badge for any unread DM. Row-level mention detection remains separate; this note is only about the section summary styling. + ### RawPacketList always scrolls to bottom `RawPacketList` unconditionally scrolls to the latest packet on every update. This is intentional — the packet feed is a live status display, not an interactive log meant for lingering or long-term analysis. Users watching it want to see the newest packet, not hold a scroll position. diff --git a/frontend/prebuilt/assets/BotCodeEditor-CBFX17Kn.js b/frontend/prebuilt/assets/BotCodeEditor-DUWnXYXA.js similarity index 99% rename from frontend/prebuilt/assets/BotCodeEditor-CBFX17Kn.js rename to frontend/prebuilt/assets/BotCodeEditor-DUWnXYXA.js index 344d57af8a8d7d0503919f71be75672c641b626c..5ff3baec4f40d39419074124db2e8dc911f2ca26 100644 GIT binary patch delta 59 zcmeA^DARpVW`YWbi$zJkVOoSwqfV<1W2+8Rs}6Ik4oj;JYpV|1RvmVK1p$}P@Vtn~ O2uHoFV!hnN0xke8wh^BI delta 59 zcmeA^DARpVW`YWblb5lNsk>2eqfV<1W2+8Rs}6Ik4oj;JYpV|1RvmVK1p#L#w+KUX O?>xP%V!hnN0xkd)0T9gq diff --git a/frontend/prebuilt/assets/BotCodeEditor-CBFX17Kn.js.map b/frontend/prebuilt/assets/BotCodeEditor-DUWnXYXA.js.map similarity index 99% rename from frontend/prebuilt/assets/BotCodeEditor-CBFX17Kn.js.map rename to frontend/prebuilt/assets/BotCodeEditor-DUWnXYXA.js.map index 4810b403e443d6d66fb88e44066986b197cce6e2..16dde522b9675eae9bfc809ab3b85a4f864b65d5 100644 GIT binary patch delta 108 zcmV~$yA6U+06@|BIej^Y-yg_f9wz_* diff --git a/frontend/prebuilt/assets/CrackerPanel-RTfofPO9.js b/frontend/prebuilt/assets/CrackerPanel-CqHBN9pz.js similarity index 99% rename from frontend/prebuilt/assets/CrackerPanel-RTfofPO9.js rename to frontend/prebuilt/assets/CrackerPanel-CqHBN9pz.js index 24374ab62456deeb5cb3a28b7d8a47d0aa627de3..99ec8083ea74d2644d31b15218789233dc471497 100644 GIT binary patch delta 41 xcmZqr%--~weL@$9i$zJkVOoSw2eF7M2#)Eo`j3yv~IlPJWgJReD*)O0@vZPzv4v delta 35 qcmbOF7M2#)Eo`j3yg?yp`Dp?EmU>ymO0@va<_hQl diff --git a/frontend/prebuilt/assets/MapView-DG71Do9A.js b/frontend/prebuilt/assets/MapView-CgY0ZIub.js similarity index 92% rename from frontend/prebuilt/assets/MapView-DG71Do9A.js rename to frontend/prebuilt/assets/MapView-CgY0ZIub.js index 7050c294d6f01480f959b0d81ae1d5d065ba3441..c49cd9aeee987273bcbc7620f41b585fb3f17adc 100644 GIT binary patch delta 72 zcmew+|4n{^IfsiyNxorPgwI5KO%CV4uqdPKRL_Y8>KtaK-W4XM{+<)(D{we@M3i}C cm*s9eF3&IEoE~Wq{QSn53O^y)1Y@^7kpvZ{@>KtArY5qQCQLYo`D{%NZg`@|U cRzz$(F3&IE;%;u}l5gp#msPBnn^?dF0C5u+dH?_b diff --git a/frontend/prebuilt/assets/MapView-DG71Do9A.js.map b/frontend/prebuilt/assets/MapView-CgY0ZIub.js.map similarity index 99% rename from frontend/prebuilt/assets/MapView-DG71Do9A.js.map rename to frontend/prebuilt/assets/MapView-CgY0ZIub.js.map index 363646886ef08be8bd10be375382b1247dc750b6..662552a5ba02759588f14efed70b0ced5622a907 100644 GIT binary patch delta 19 acmbPVGQVVkEQfP?q(PKtY0^g3UzPw%*9VdS delta 19 acmbPVGQVVkEQgD`xuHwGrQ=4`UzPwzg$D%y diff --git a/frontend/prebuilt/assets/NeighborsMiniMap-D6bvxuvI.js b/frontend/prebuilt/assets/NeighborsMiniMap-nhWnbDjS.js similarity index 85% rename from frontend/prebuilt/assets/NeighborsMiniMap-D6bvxuvI.js rename to frontend/prebuilt/assets/NeighborsMiniMap-nhWnbDjS.js index 65aca5069aadbdf5984ea1717592b4c04c734be0..89497c859983a4c59073019e86140398ca62b8dd 100644 GIT binary patch delta 60 zcmZqUZR4FF&EaBEl5dz6;WJU$mBTqOEXpW5)pKHjI)|C5cZG?mzvssJ*I5PfGQ#tc PT(W}ovWoR`6AQQiGe8p_ delta 60 zcmZqUZR4FF&Ee!_>|^R~R6J4Hl_SJ2+bFUsC~{(fI)_(Dn!it3l3t|9Mpa<9h delta 19 acmeyT`%ia*8i$KnQdvc5nde5m3t|9LRR`Dr diff --git a/frontend/prebuilt/assets/PathRouteMap-DiGw6Qka.js b/frontend/prebuilt/assets/PathRouteMap-BgANOQRu.js similarity index 95% rename from frontend/prebuilt/assets/PathRouteMap-DiGw6Qka.js rename to frontend/prebuilt/assets/PathRouteMap-BgANOQRu.js index cb9632d87524473937e38944235ebcda791389e6..143fc342d3756662e2aa38103d9065d2d687272e 100644 GIT binary patch delta 60 zcmbOuH%D%QCWnhfNxorPgwI67U=HWNuqdPKRL_Y$3LH)z5oI3PWw{%-MDPhXr91lh Q2L_euWfkk?CKhl3094)+ssI20 delta 60 zcmbOuH%D%QCWn)kv5%>{QSn5>V2%*KY@^7kpvZ|m3LJh;A?d-T6%iY^MDPi?WV)A| Q1!gDeWfkk?CKhl307m~4egFUf diff --git a/frontend/prebuilt/assets/PathRouteMap-DiGw6Qka.js.map b/frontend/prebuilt/assets/PathRouteMap-BgANOQRu.js.map similarity index 99% rename from frontend/prebuilt/assets/PathRouteMap-DiGw6Qka.js.map rename to frontend/prebuilt/assets/PathRouteMap-BgANOQRu.js.map index 1a0c65e0049e066be29ec465862ab5c17b49387c..df9a94780f1d5f4b7706dba30a2f217d2b807268 100644 GIT binary patch delta 19 acmeyC`z?2Z5{FZ|qo03ZQ0YdkGiCr*ng{&= delta 19 acmeyC`z?2Z5{FBsd%0O)cH%~@GiCr+-v}H4 diff --git a/frontend/prebuilt/assets/Popup-JtfOLvZE.js b/frontend/prebuilt/assets/Popup-65Kx45OI.js similarity index 81% rename from frontend/prebuilt/assets/Popup-JtfOLvZE.js rename to frontend/prebuilt/assets/Popup-65Kx45OI.js index 460ebc75919d8af5bc46b369911b2f98b355cf2a..4c3c696b4a4f86295a950bfb72d7e7b65841ffe8 100644 GIT binary patch delta 48 zcmbQiK7)ON35RoFSd>wAs^>%-1r8UBl6=Fo2%n89D$D|Arrs4Mrv9FKS;cy}i3MB$ DUmy-8 delta 48 zcmbQiK7)ON2}g)uwoznNP~=1#1r8@KV;@s@qvDMzD$D|2C29UXWl^qrS;cy}i3MB$ DV>S+o diff --git a/frontend/prebuilt/assets/Popup-JtfOLvZE.js.map b/frontend/prebuilt/assets/Popup-65Kx45OI.js.map similarity index 97% rename from frontend/prebuilt/assets/Popup-JtfOLvZE.js.map rename to frontend/prebuilt/assets/Popup-65Kx45OI.js.map index 10a1dd64f55120b1119eea66d6029d63609589a1..89e0d0df402b8155485a2ad7e0584c0497a51a3e 100644 GIT binary patch delta 19 acmcbra8+S~G>4g~cZG?mzvo6}BLM(Hi3Q^T delta 19 acmcbra8+S~G>2D7n!it3lvQs@Lt~2B?Gxe@8G4=PH`1}|LOwHu2jH>K~-eHxxX<3^eGP>$>zzp83XUJV0IRbsKL7v# delta 128 zcmX?okMZ<9#tA{nE@nw(6{TgKdRfIvI!cMf#i=F5`k8qtsTI0TUdBGA?ncEEOAR?f z{IZQAtAZjYt~2EDDoOMADT{KQ`1}|LOwHu2jH>Jzp`J-zo?e?DGP>$>zzp83XUJV0Hc>IrvLx| diff --git a/frontend/prebuilt/assets/RepeaterDashboard-bua8zxo_.js.map b/frontend/prebuilt/assets/RepeaterDashboard-B86OTQtd.js.map similarity index 99% rename from frontend/prebuilt/assets/RepeaterDashboard-bua8zxo_.js.map rename to frontend/prebuilt/assets/RepeaterDashboard-B86OTQtd.js.map index 3a16d650058738732a7d3dc7b62ad0f90d007507..15b861a0f5264a6eb18f3da328ec43671947b0b0 100644 GIT binary patch delta 25 gcmeycm-WM5)`l&NkK=fqEX@2v0!vc#vWk^z0h8nk4gdfE delta 25 gcmeycm-WM5)`l&NkK=fgN)s)rD)Qs?vWk^z0h^!-!T{QSnCq$-)B8#pY$kxn|yaS;cy}i3MB$?1KyW diff --git a/frontend/prebuilt/assets/SearchView-Cs7v3m6K.js.map b/frontend/prebuilt/assets/SearchView-BFMnGDyJ.js.map similarity index 99% rename from frontend/prebuilt/assets/SearchView-Cs7v3m6K.js.map rename to frontend/prebuilt/assets/SearchView-BFMnGDyJ.js.map index 43a110169b64d223a129b1d1ebcb66c0c3e5fbeb..a8831582de18217775e8d086fce1a5997ca9227c 100644 GIT binary patch delta 21 ccmey?%=oRDae@MelbdgzyGy0lMvZfh09~jECIA2c delta 21 ccmey?%=oRDae@MebFq1uaju#7MvZfh0A2wI82|tP diff --git a/frontend/prebuilt/assets/SettingsModal-C-alEUMR.js b/frontend/prebuilt/assets/SettingsModal-CmnPdiWo.js similarity index 99% rename from frontend/prebuilt/assets/SettingsModal-C-alEUMR.js rename to frontend/prebuilt/assets/SettingsModal-CmnPdiWo.js index c5ca5478bbadb22e3a1b822ac2345a136a6084e6..10553f24e4d3a4d42f11a20326a8bdaec50a8c91 100644 GIT binary patch delta 131 zcmX?qo8|m%mI?mKE}`Lh5s?v&dRfIvI!cMf#i=F5`k8qtsTH~|7A5(HX%RjfiK~-eHxxX<3_BGO|Q*z)aY@HJW)Zdvcm#qN!=>bO8lM>B;_kMJDr`@NWMt X!FW`JKQ}KRB{Mu9Xp>%UVgVNby{;@f delta 131 zcmX?qo8|m%mI?mK&Q5L-hUVURdRfIvI!cMf#i=F5`k8qtsTI0TUdBGA?ncEMiJzp`J-zo?e?*GO|Q*z)aY@HJW)ZyMcv;p{1q8bO8lM>B;_kMJDr`@NWMt X!FW`JUpFzwHPkl%UVgVNb{aYy- diff --git a/frontend/prebuilt/assets/SettingsModal-C-alEUMR.js.map b/frontend/prebuilt/assets/SettingsModal-CmnPdiWo.js.map similarity index 99% rename from frontend/prebuilt/assets/SettingsModal-C-alEUMR.js.map rename to frontend/prebuilt/assets/SettingsModal-CmnPdiWo.js.map index 775484283b645a2ab4703b8b9c08d326b553b2b4..3979c5f4dcd52a63e5d99eef1def1f5d5f332924 100644 GIT binary patch delta 27 icmccAz;mgAr=f+hg=q@28&7UtKuTtKzFtcj%%oIkX}}?QY`?95(y3f diff --git a/frontend/prebuilt/assets/VisualizerView-BBRdi0S-.js b/frontend/prebuilt/assets/VisualizerView-BX96-zH4.js similarity index 99% rename from frontend/prebuilt/assets/VisualizerView-BBRdi0S-.js rename to frontend/prebuilt/assets/VisualizerView-BX96-zH4.js index e187a6a10f172250d5c2ab1711b20252d3673209..fb827d23e8f128c057928f4985bddcbefe0add89 100644 GIT binary patch delta 81 zcmey^tn#s0WkMg1i$zJkVOoTbURJTvWJadV&AyE7zKkHu1jNih%mT!$K+Fcj>_E&h Y-ItMbDu0BfnQoPb2~eM2Zejr!01Y1(1poj5 delta 81 zcmey^tn#s0WkMg1lb5lNsk>3JURJTvWJadV&AyE7zKkHu1jNih%mT!$K+Fcj>_E&h Y-ItMbD!)@uN~S@uE>NFdZejr!0P&I*z5oCK diff --git a/frontend/prebuilt/assets/VisualizerView-BBRdi0S-.js.map b/frontend/prebuilt/assets/VisualizerView-BX96-zH4.js.map similarity index 99% rename from frontend/prebuilt/assets/VisualizerView-BBRdi0S-.js.map rename to frontend/prebuilt/assets/VisualizerView-BX96-zH4.js.map index a0697740dfa81fd20e67c9d849584c9a1b7f733f..41609cb16e07dd10d57656d62bb2b1d112ced4f5 100644 GIT binary patch delta 163 zcmWO0w++Go002RebIv)M_{I%FVgp8C78*qHJbOe48$A diff --git a/frontend/prebuilt/assets/hooks-BHXvHkvm.js b/frontend/prebuilt/assets/hooks-BHXvHkvm.js new file mode 100644 index 0000000000000000000000000000000000000000..ae98588f0ca1fb14b3aaea7fe6f6e4939fb3b6ef GIT binary patch literal 157 zcmW-ay$*sf6h?QRqQM0QAUc^47j?h{2Te4@t-)KZ(4XAi5|V~@7nr~Eog%rlEUkjq|pqMG$4>i%_vE8 s#q9*+QV6(M{8UCov0=`5Y~jkSmHP9z*N3k$oy(3@GgRH6X6JH^{tG@gAOHXW literal 0 HcmV?d00001 diff --git a/frontend/prebuilt/assets/hooks-NBTgSuxX.js.map b/frontend/prebuilt/assets/hooks-BHXvHkvm.js.map similarity index 90% rename from frontend/prebuilt/assets/hooks-NBTgSuxX.js.map rename to frontend/prebuilt/assets/hooks-BHXvHkvm.js.map index edc92738d006f505c6b4135bc81924450ea21a14..cfe07d5d07ccb6eee5ac4bf484099a531a8a87f8 100644 GIT binary patch delta 19 acmaFC@q%N5G>4N%M43l+S?)$4y4NP2K-MZ`vBR~7(Ahz67Z diff --git a/frontend/prebuilt/assets/hooks-NBTgSuxX.js b/frontend/prebuilt/assets/hooks-NBTgSuxX.js deleted file mode 100644 index 6720f03b3f14476da4b18e701296928e1e243688..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 157 zcmW-ay$XXc7)5uVBG^TT7_fAbE-r;)9jt{q1{*ci{O~1#(BQjQo$tBlaG@30I3rws zn8)S)TwQLt$-X8o_^}0ts6loeK#7f87#$X7aa+%M_iMU?9GvVeR}w*z24L2KL^eq? sLG{Y~UI?&T+*(A0oRCotdiJ1Nb2+}6>N+Vk8(f#|_;migIrnSy2jF@()c^nh diff --git a/frontend/prebuilt/assets/index-BJ3L5G2s.js b/frontend/prebuilt/assets/index-D8to1fXL.js similarity index 90% rename from frontend/prebuilt/assets/index-BJ3L5G2s.js rename to frontend/prebuilt/assets/index-D8to1fXL.js index d9be552f48855a87315a9a901cb5413f343a2cac..f92fcd8cee9975c9c3d0777fa68d0c15b8f1d5d5 100644 GIT binary patch delta 3648 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 T$*hHQiZdek9rCmU+s6DKVWn$l diff --git a/frontend/prebuilt/assets/index-BJ3L5G2s.js.map b/frontend/prebuilt/assets/index-D8to1fXL.js.map similarity index 95% rename from frontend/prebuilt/assets/index-BJ3L5G2s.js.map rename to frontend/prebuilt/assets/index-D8to1fXL.js.map index cc0130d2b0471e3a768aad9c6803e94f48fa80c5..41dc715c2559f7da11b8322c3b86b2b725ab2a25 100644 GIT binary patch delta 355 zcmcclrh&2dLjz+&3u6mY3v&xg3u_Bo3wsO47EYdZjQ-ns*KxA3PX8^y!aM!23X9@;1)5W)P>TOTj%sGW|x}*?PB~ym)bjGcmVn~X&f7`;@ z&Bv4%-rjte6NtHhm>YmDKA>HJIO zxrCF8QWHy36_WGw%2JDp6H7Al^QK?$6>yloo>M@GxmMG9`|p=>W-O%Wm=4s@*S`Io a-1hD7%It8>U70=w%fv)dB#oi-r3D delta 207 zcmeDE(7<@}O#@>?3u6mY3v&xg3u_Bo3wsO47EYdZjK14>*KxA3PVW+6Q=C41E2rM} zMVmRNFm5l~%Gu4wWE$4ae1sE-xqz4(h-A*fLIWSg@9NXh()$D9}#t{ zWlU}Naufq%aUhleVo4yD0%B<(mH}ef?Ou*@Pv&h8c_0_cHQnK`sQ&i&*K+@v!OZDE fW_Ww{2f6LpALOUH^E!DM`vQs_vvWoR`6AQQiS1b-_ delta 44 zcmZ3ok#osL&IzU*PF}`7rtU_?jkc|}j9YD)!XpGi{IZQAtAZl+vWoR`6AQQiOQjAd diff --git a/frontend/prebuilt/assets/leaflet-TNk2YzRY.js.map b/frontend/prebuilt/assets/leaflet-CQVZ2keI.js.map similarity index 99% rename from frontend/prebuilt/assets/leaflet-TNk2YzRY.js.map rename to frontend/prebuilt/assets/leaflet-CQVZ2keI.js.map index 10f8bed493e689a81336582c67414b8b82fd4a81..e25fb69647e8459e1e4cc5699e03c2cf49b9b1ed 100644 GIT binary patch delta 51 zcmZ4ZSZm>9tqHOm&VgZ3M%k&JjjFAxjIF9nt*Xqesw}OltgWhSt*Y#;svNDVoLg17 HYBmA@x@`}C delta 51 zcmZ4ZSZm>9tqHOmA%59LkySyFjjFAxjIF9nt*Xqesw}OltgWhSt*Y#;svNDVoLg17 HYBmA@!DSDv diff --git a/frontend/prebuilt/assets/separator-ChUIbJIJ.js b/frontend/prebuilt/assets/separator-CqKVy-fj.js similarity index 89% rename from frontend/prebuilt/assets/separator-ChUIbJIJ.js rename to frontend/prebuilt/assets/separator-CqKVy-fj.js index 6229c01a9685777f02760b65fdd73b5721826bba..311aeda0b5e62ee8a00366d40f6da1072668d5b1 100644 GIT binary patch delta 35 qcmZ3(v4&%U0f&o4NxorPgwIBcr_B6?-eHxxX<2$%#d^7k1zZ5sunV*R delta 35 qcmZ3(v4&%U0f&>9v5%>{QSnBLr_B5rp`J-zo?d!c#d^7k1zZ5fBntxo diff --git a/frontend/prebuilt/assets/separator-ChUIbJIJ.js.map b/frontend/prebuilt/assets/separator-CqKVy-fj.js.map similarity index 98% rename from frontend/prebuilt/assets/separator-ChUIbJIJ.js.map rename to frontend/prebuilt/assets/separator-CqKVy-fj.js.map index d6c896613c06959e53503be9613b2ccba455767d..0774ff3bb456c3b2ed6ac84f200edc034fec00a2 100644 GIT binary patch delta 18 ZcmX@4en@?S0(+r%Sfy@S)<*T&!T>>32ABW< delta 18 ZcmX@4en@?S0((ZNXOfqv*GBc(!T>*|22}t6 diff --git a/frontend/prebuilt/index.html b/frontend/prebuilt/index.html index 3ba8dfaeb4f29231671f20012e9094cc208444dc..9526f1581b5da87fdfb318984105f845157154a1 100644 GIT binary patch delta 20 bcmX>pdQx delta 20 bcmX>pdQx ({ @@ -35,6 +36,11 @@ const mockContact: Contact = { first_seen: null, }; +const mockToast = toast as unknown as { + success: ReturnType; + error: ReturnType; +}; + describe('NewMessageModal form reset', () => { const onClose = vi.fn(); const onSelectConversation = vi.fn(); @@ -137,6 +143,24 @@ describe('NewMessageModal form reset', () => { }); expect(onClose).toHaveBeenCalled(); }); + + it('toasts when creation fails', async () => { + const user = userEvent.setup(); + onCreateChannel.mockRejectedValueOnce(new Error('Bad key')); + renderModal(); + await switchToTab(user, 'Room'); + + await user.type(screen.getByPlaceholderText('Room name'), 'MyRoom'); + await user.type(screen.getByPlaceholderText('Pre-shared key (hex)'), 'cc'.repeat(16)); + await user.click(screen.getByRole('button', { name: 'Create' })); + + await waitFor(() => { + expect(mockToast.error).toHaveBeenCalledWith('Failed to create conversation', { + description: 'Bad key', + }); + }); + expect(screen.getByText('Bad key')).toBeTruthy(); + }); }); describe('tab switching resets form', () => { diff --git a/tests/test_packets_router.py b/tests/test_packets_router.py index 536a93fc..edd89823 100644 --- a/tests/test_packets_router.py +++ b/tests/test_packets_router.py @@ -72,7 +72,7 @@ class TestDecryptHistoricalPackets: }, ) - assert response.status_code == 200 + assert response.status_code == 202 data = response.json() assert data["started"] is True assert data["total_packets"] == 5 @@ -91,7 +91,7 @@ class TestDecryptHistoricalPackets: }, ) - assert response.status_code == 200 + assert response.status_code == 202 data = response.json() assert data["started"] is True assert data["total_packets"] == 3 @@ -107,10 +107,9 @@ class TestDecryptHistoricalPackets: }, ) - assert response.status_code == 200 + assert response.status_code == 400 data = response.json() - assert data["started"] is False - assert "invalid" in data["message"].lower() + assert "invalid" in data["detail"].lower() @pytest.mark.asyncio async def test_channel_decrypt_wrong_key_length(self, test_db, client): @@ -123,10 +122,9 @@ class TestDecryptHistoricalPackets: }, ) - assert response.status_code == 200 + assert response.status_code == 400 data = response.json() - assert data["started"] is False - assert "16 bytes" in data["message"] + assert "16 bytes" in data["detail"] @pytest.mark.asyncio async def test_channel_decrypt_no_key_or_name(self, test_db, client): @@ -136,10 +134,9 @@ class TestDecryptHistoricalPackets: json={"key_type": "channel"}, ) - assert response.status_code == 200 + assert response.status_code == 400 data = response.json() - assert data["started"] is False - assert "must provide" in data["message"].lower() + assert "must provide" in data["detail"].lower() @pytest.mark.asyncio async def test_channel_decrypt_no_undecrypted_packets(self, test_db, client): @@ -172,7 +169,7 @@ class TestDecryptHistoricalPackets: }, ) - assert response.status_code == 200 + assert response.status_code == 202 assert response.json()["started"] is True @pytest.mark.asyncio @@ -186,10 +183,9 @@ class TestDecryptHistoricalPackets: }, ) - assert response.status_code == 200 + assert response.status_code == 400 data = response.json() - assert data["started"] is False - assert "private_key" in data["message"].lower() + assert "private_key" in data["detail"].lower() @pytest.mark.asyncio async def test_contact_decrypt_missing_contact_key(self, test_db, client): @@ -202,10 +198,9 @@ class TestDecryptHistoricalPackets: }, ) - assert response.status_code == 200 + assert response.status_code == 400 data = response.json() - assert data["started"] is False - assert "contact_public_key" in data["message"].lower() + assert "contact_public_key" in data["detail"].lower() @pytest.mark.asyncio async def test_contact_decrypt_wrong_private_key_length(self, test_db, client): @@ -219,10 +214,9 @@ class TestDecryptHistoricalPackets: }, ) - assert response.status_code == 200 + assert response.status_code == 400 data = response.json() - assert data["started"] is False - assert "64 bytes" in data["message"] + assert "64 bytes" in data["detail"] @pytest.mark.asyncio async def test_contact_decrypt_wrong_public_key_length(self, test_db, client): @@ -236,10 +230,9 @@ class TestDecryptHistoricalPackets: }, ) - assert response.status_code == 200 + assert response.status_code == 400 data = response.json() - assert data["started"] is False - assert "32 bytes" in data["message"] + assert "32 bytes" in data["detail"] @pytest.mark.asyncio async def test_contact_decrypt_invalid_hex(self, test_db, client): @@ -253,10 +246,9 @@ class TestDecryptHistoricalPackets: }, ) - assert response.status_code == 200 + assert response.status_code == 400 data = response.json() - assert data["started"] is False - assert "invalid" in data["message"].lower() + assert "invalid" in data["detail"].lower() @pytest.mark.asyncio async def test_invalid_key_type(self, test_db, client): @@ -266,10 +258,9 @@ class TestDecryptHistoricalPackets: json={"key_type": "invalid"}, ) - assert response.status_code == 200 + assert response.status_code == 400 data = response.json() - assert data["started"] is False - assert "key_type" in data["message"].lower() + assert "key_type" in data["detail"].lower() class TestRunHistoricalChannelDecryption: