From c9781b666a69dad224b83f43dbad167be232f393 Mon Sep 17 00:00:00 2001 From: edward sharp Date: Mon, 2 Jun 2025 23:13:35 -0400 Subject: [PATCH 01/26] oh a bunch of v2 stuff i guess? mostly cargo run --example v2 but really lean into the monorepo :/ so init worker and shared dir (unclear if this is a good idea or not) --- .gitignore | 1 + Cargo.lock | 1516 +++++++++++++++++++++++++++++------ Cargo.toml | 4 +- makefile | 60 ++ server/Cargo.toml | 10 +- server/examples/sqlitedb.rs | 28 + server/examples/v2.rs | 192 +++++ shared/Cargo.toml | 11 + shared/src/db.rs | 52 ++ shared/src/db_worker.rs | 78 ++ shared/src/lib.rs | 3 + shared/src/models.rs | 9 + worker/Cargo.toml | 15 + worker/src/job_handlers.rs | 36 + worker/src/main.rs | 37 + 15 files changed, 1820 insertions(+), 232 deletions(-) create mode 100644 makefile create mode 100644 server/examples/sqlitedb.rs create mode 100644 server/examples/v2.rs create mode 100644 shared/Cargo.toml create mode 100644 shared/src/db.rs create mode 100644 shared/src/db_worker.rs create mode 100644 shared/src/lib.rs create mode 100644 shared/src/models.rs create mode 100644 worker/Cargo.toml create mode 100644 worker/src/job_handlers.rs create mode 100644 worker/src/main.rs diff --git a/.gitignore b/.gitignore index d47e0bd..282e8ef 100644 --- a/.gitignore +++ b/.gitignore @@ -50,3 +50,4 @@ Thumbs.db /server/target /target /server/library +queue.db diff --git a/Cargo.lock b/Cargo.lock index 60d777d..8c3ebd8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,16 +1,49 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 + +[[package]] +name = "ahash" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] [[package]] name = "aho-corasick" -version = "0.7.18" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + [[package]] name = "atty" version = "0.2.14" @@ -34,12 +67,24 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + [[package]] name = "bitflags" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" +[[package]] +name = "bitflags" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" + [[package]] name = "block-buffer" version = "0.10.2" @@ -49,6 +94,12 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bumpalo" +version = "3.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" + [[package]] name = "byteorder" version = "1.4.3" @@ -61,12 +112,41 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b700ce4376041dcd0a327fd0097c41095743c4c8af8887265942faf1100bd040" +[[package]] +name = "cc" +version = "1.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0fc897dc1e865cc67c0e05a836d9d3f1df3cbe442aa4a9473b18e12624a4951" +dependencies = [ + "shlex", +] + [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chrono" +version = "0.4.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-link", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + [[package]] name = "cpufeatures" version = "0.2.3" @@ -96,6 +176,26 @@ dependencies = [ "crypto-common", ] +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if", +] + [[package]] name = "env_logger" version = "0.9.0" @@ -109,6 +209,24 @@ dependencies = [ "termcolor", ] +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "fallible-iterator" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" + +[[package]] +name = "fallible-streaming-iterator" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" + [[package]] name = "fnv" version = "1.0.7" @@ -117,62 +235,97 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "form_urlencoded" -version = "1.0.1" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" dependencies = [ - "matches", "percent-encoding", ] [[package]] -name = "futures-channel" +name = "futures" version = "0.3.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bfc52cbddcfd745bf1740338492bb0bd83d76c67b445f91c5fb29fae29ecaa1" +checksum = "ab30e97ab6aacfe635fad58f22c2bb06c8b685f7421eb1e064a729e2a5f481fa" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" dependencies = [ "futures-core", + "futures-sink", ] [[package]] name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-executor" version = "0.3.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2acedae88d38235936c3922476b10fced7b2b68136f5e3c03c2d5be348a1115" +checksum = "1d11aa21b5b587a64682c0094c2bdd4df0076c5324961a40cc3abd7f37930528" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-macro" -version = "0.3.23" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0db9cce532b0eae2ccf2766ab246f114b56b9cf6d445e00c2549fbc100ca045d" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.101", ] [[package]] name = "futures-sink" -version = "0.3.23" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca0bae1fe9752cf7fd9b0064c674ae63f97b37bc714d745cbde0afb7ec4e6765" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" [[package]] name = "futures-task" -version = "0.3.23" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "842fc63b931f4056a24d59de13fb1272134ce261816e063e634ad0c15cdc5306" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" [[package]] name = "futures-util" -version = "0.3.23" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0828a5471e340229c11c77ca80017937ce3c58cb788a17e5f1c2d5c485a9577" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ + "futures-channel", "futures-core", + "futures-io", "futures-macro", "futures-sink", "futures-task", + "memchr", "pin-project-lite", "pin-utils", "slab", @@ -201,243 +354,596 @@ dependencies = [ [[package]] name = "glob" -version = "0.3.0" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" +checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" [[package]] -name = "hermit-abi" -version = "0.1.18" +name = "h2" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c" +checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" dependencies = [ - "libc", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", ] [[package]] -name = "http" -version = "0.2.4" +name = "hashbrown" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "527e8c9ac747e28542699a951517aa9a6945af506cd1f2e1b53a576c17b6cc11" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" dependencies = [ - "bytes", - "fnv", - "itoa", + "ahash", + "allocator-api2", ] [[package]] -name = "httparse" -version = "1.4.1" +name = "hashbrown" +version = "0.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3a87b616e37e93c22fb19bcd386f02f3af5ea98a25670ad0fce773de23c5e68" +checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3" [[package]] -name = "humantime" -version = "2.1.0" +name = "hashlink" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" +checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7" +dependencies = [ + "hashbrown 0.14.5", +] [[package]] -name = "idna" -version = "0.2.3" +name = "headers" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" +checksum = "06683b93020a07e3dbcf5f8c0f6d40080d725bea7936fc01ad345c01b97dc270" dependencies = [ - "matches", - "unicode-bidi", - "unicode-normalization", + "base64 0.21.7", + "bytes", + "headers-core", + "http", + "httpdate", + "mime", + "sha1", ] [[package]] -name = "itoa" -version = "0.4.7" +name = "headers-core" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" +checksum = "e7f66481bfee273957b1f20485a4ff3362987f85b2c236580d81b4eb7a326429" +dependencies = [ + "http", +] [[package]] -name = "libc" -version = "0.2.132" +name = "hermit-abi" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8371e4e5341c3a96db127eb2465ac681ced4c433e01dd0e938adbef26ba93ba5" +checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c" +dependencies = [ + "libc", +] [[package]] -name = "lock_api" -version = "0.4.7" +name = "http" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53" +checksum = "527e8c9ac747e28542699a951517aa9a6945af506cd1f2e1b53a576c17b6cc11" dependencies = [ - "autocfg", - "scopeguard", + "bytes", + "fnv", + "itoa 0.4.7", ] [[package]] -name = "log" -version = "0.4.17" +name = "http-body" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" dependencies = [ - "cfg-if", + "bytes", + "http", + "pin-project-lite", ] [[package]] -name = "matches" -version = "0.1.8" +name = "httparse" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" [[package]] -name = "memchr" -version = "2.4.0" +name = "httpdate" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] -name = "mio" -version = "0.8.4" +name = "humantime" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "hyper" +version = "0.14.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b61cf2d1aebcf6e6352c97b81dc2244ca29194be1b276f5d8ad5c6330fffb11" dependencies = [ - "libc", - "log", - "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys", + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa 0.4.7", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", ] [[package]] -name = "num_cpus" -version = "1.13.0" +name = "iana-time-zone" +version = "0.1.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" +checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" dependencies = [ - "hermit-abi", - "libc", + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", ] [[package]] -name = "once_cell" -version = "1.7.2" +name = "iana-time-zone-haiku" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af8b08b04175473088b46763e51ee54da5f9a164bc162f615b91bc179dbf15a3" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] [[package]] -name = "parking_lot" -version = "0.12.1" +name = "icu_collections" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" dependencies = [ - "lock_api", - "parking_lot_core", + "displaydoc", + "potential_utf", + "yoke", + "zerofrom", + "zerovec", ] [[package]] -name = "parking_lot_core" -version = "0.9.3" +name = "icu_locale_core" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929" +checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-sys", + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", ] [[package]] -name = "percent-encoding" -version = "2.1.0" +name = "icu_normalizer" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" +checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] [[package]] -name = "pin-project-lite" -version = "0.2.6" +name = "icu_normalizer_data" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc0e1f259c92177c30a4c9d177246edd0a3568b25756a977d0632cf8fa37e905" +checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" [[package]] -name = "pin-utils" -version = "0.1.0" +name = "icu_properties" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "potential_utf", + "zerotrie", + "zerovec", +] [[package]] -name = "ppv-lite86" -version = "0.2.10" +name = "icu_properties_data" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" +checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" [[package]] -name = "proc-macro2" -version = "1.0.27" +name = "icu_provider" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0d8caf72986c1a598726adc988bb5984792ef84f5ee5aa50209145ee8077038" +checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" dependencies = [ - "unicode-xid", + "displaydoc", + "icu_locale_core", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", ] [[package]] -name = "quote" -version = "1.0.9" +name = "idna" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" dependencies = [ - "proc-macro2", + "idna_adapter", + "smallvec", + "utf8_iter", ] [[package]] -name = "rand" -version = "0.8.3" +name = "idna_adapter" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" dependencies = [ - "libc", - "rand_chacha", - "rand_core", - "rand_hc", + "icu_normalizer", + "icu_properties", ] [[package]] -name = "rand_chacha" -version = "0.3.0" +name = "indexmap" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d" +checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" dependencies = [ - "ppv-lite86", - "rand_core", + "equivalent", + "hashbrown 0.15.3", ] [[package]] -name = "rand_core" -version = "0.6.2" +name = "itoa" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34cf66eb183df1c5876e2dcf6b13d57340741e8dc255b48e40a26de954d06ae7" -dependencies = [ - "getrandom", -] +checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" [[package]] -name = "rand_hc" -version = "0.3.0" +name = "itoa" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73" -dependencies = [ - "rand_core", -] +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] -name = "redox_syscall" -version = "0.2.8" +name = "js-sys" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "742739e41cd49414de871ea5e549afb7e2a3ac77b589bcbebe8c82fab37147fc" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" dependencies = [ - "bitflags", + "once_cell", + "wasm-bindgen", ] [[package]] -name = "regex" -version = "1.5.6" +name = "libc" +version = "0.2.172" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" + +[[package]] +name = "libsqlite3-sys" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4e226dcd58b4be396f7bd3c20da8fdee2911400705297ba7d2d7cc2c30f716" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "litemap" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" + +[[package]] +name = "lock_api" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "mime_guess" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" +dependencies = [ + "mime", + "unicase", +] + +[[package]] +name = "mio" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf" +dependencies = [ + "libc", + "log", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys 0.36.1", +] + +[[package]] +name = "multer" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01acbdc23469fd8fe07ab135923371d5f5a422fbf9c522158677c8eb15bc51c2" +dependencies = [ + "bytes", + "encoding_rs", + "futures-util", + "http", + "httparse", + "log", + "memchr", + "mime", + "spin", + "version_check", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-sys 0.36.1", +] + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pin-project" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + +[[package]] +name = "potential_utf" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" +dependencies = [ + "zerovec", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" + +[[package]] +name = "proc-macro2" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", + "rand_hc", +] + +[[package]] +name = "rand_chacha" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d83f127d94bdbcda4c8cc2e50f6f84f4b611f69c902699ca385a39c3a75f9ff1" +checksum = "34cf66eb183df1c5876e2dcf6b13d57340741e8dc255b48e40a26de954d06ae7" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_hc" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73" +dependencies = [ + "rand_core", +] + +[[package]] +name = "redox_syscall" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "742739e41cd49414de871ea5e549afb7e2a3ac77b589bcbebe8c82fab37147fc" +dependencies = [ + "bitflags 1.2.1", +] + +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", @@ -446,9 +952,38 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.26" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49b3de9ec5dc0a3417da371aab17d729997c15010e7fd24ff707773a33bddb64" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "rusqlite" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a78046161564f5e7cd9008aff3b2990b3850dc8e0349119b98e8f251e099f24d" +dependencies = [ + "bitflags 2.9.1", + "fallible-iterator", + "fallible-streaming-iterator", + "hashlink", + "libsqlite3-sys", + "smallvec", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +dependencies = [ + "base64 0.21.7", +] + +[[package]] +name = "rustversion" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" [[package]] name = "ryu" @@ -456,39 +991,58 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" +[[package]] +name = "scoped-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" + [[package]] name = "scopeguard" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "serde" -version = "1.0.126" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec7505abeacaec74ae4778d9d9328fe5a5d04253220a85c4ee022239fc996d03" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.126" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "963a7dbc9895aeac7ac90e74f34a5d5261828f79df35cbed41e10189d3804d43" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.101", ] [[package]] name = "serde_json" -version = "1.0.64" +version = "1.0.140" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "799e97dc9fdae36a5c8b8f2cae9ce2ee9fdce2058c57a93e6099d919fd982f79" +checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" dependencies = [ - "itoa", + "itoa 1.0.15", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa 1.0.15", "ryu", "serde", ] @@ -505,25 +1059,56 @@ dependencies = [ ] [[package]] -name = "signal-hook-registry" +name = "sha1" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "006769ba83e921b3085caa8334186b00cf92b4cb1a6cf4632fbccc8eff5c7549" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "shared" +version = "0.1.0" +dependencies = [ + "chrono", + "rusqlite", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "shlex" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16f1d0fef1604ba8f7a073c7e701f213e056707210e9020af4528e0101ce11a6" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook-registry" +version = "1.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410" dependencies = [ "libc", ] [[package]] name = "slab" -version = "0.4.3" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f173ac3d1a7e3b28003f40de0b5ce7fe2710f9b9dc3fc38664cebee46b3b6527" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] [[package]] name = "smallvec" -version = "1.6.1" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" +checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" [[package]] name = "socket2" @@ -535,6 +1120,18 @@ dependencies = [ "winapi", ] +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + [[package]] name = "syn" version = "1.0.72" @@ -546,6 +1143,28 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "syn" +version = "2.0.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + [[package]] name = "termcolor" version = "1.1.2" @@ -557,82 +1176,145 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.25" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa6f76457f59514c7eeb4e59d891395fab0b2fd1d40723ae737d64153392e9c6" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.25" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a36768c0fbf1bb15eca10defa29526bda730a2376c2ab4393ccfa16fb1a318d" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.101", ] [[package]] -name = "tinyvec" -version = "1.2.0" +name = "tinystr" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b5220f05bb7de7f3f53c7c065e1199b3172696fe2db9f9c4d8ad9b4ee74c342" +checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" dependencies = [ - "tinyvec_macros", + "displaydoc", + "zerovec", +] + +[[package]] +name = "tokio" +version = "1.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03201d01c3c27a29c8a5cee5b55a93ddae1ccf6f08f65365c2c918f8c1b76f64" +dependencies = [ + "autocfg", + "bytes", + "libc", + "memchr", + "mio", + "num_cpus", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.45.0", +] + +[[package]] +name = "tokio-macros" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.72", +] + +[[package]] +name = "tokio-stream" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-tungstenite" +version = "0.17.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f714dd15bead90401d77e04243611caec13726c2408afd5b31901dfcdcb3b181" +dependencies = [ + "futures-util", + "log", + "tokio", + "tungstenite 0.17.3", +] + +[[package]] +name = "tokio-tungstenite" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54319c93411147bced34cb5609a80e0a8e44c5999c93903a81cd866630ec0bfd" +dependencies = [ + "futures-util", + "log", + "tokio", + "tungstenite 0.18.0", +] + +[[package]] +name = "tokio-util" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", ] [[package]] -name = "tinyvec_macros" -version = "0.1.0" +name = "tower-service" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] -name = "tokio" -version = "1.20.1" +name = "tracing" +version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a8325f63a7d4774dd041e363b2409ed1c5cbbd0f867795e661df066b2b0a581" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ - "autocfg", - "bytes", - "libc", - "memchr", - "mio", - "num_cpus", - "once_cell", - "parking_lot", + "log", "pin-project-lite", - "signal-hook-registry", - "socket2", - "tokio-macros", - "winapi", + "tracing-core", ] [[package]] -name = "tokio-macros" -version = "1.8.0" +name = "tracing-core" +version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" dependencies = [ - "proc-macro2", - "quote", - "syn", + "once_cell", ] [[package]] -name = "tokio-tungstenite" -version = "0.17.2" +name = "try-lock" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f714dd15bead90401d77e04243611caec13726c2408afd5b31901dfcdcb3b181" -dependencies = [ - "futures-util", - "log", - "tokio", - "tungstenite", -] +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "tungstenite" @@ -640,7 +1322,7 @@ version = "0.17.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e27992fd6a8c29ee7eef28fc78349aa244134e10ad447ce3b9f0ac0ed0fa4ce0" dependencies = [ - "base64", + "base64 0.13.0", "byteorder", "bytes", "http", @@ -653,6 +1335,25 @@ dependencies = [ "utf-8", ] +[[package]] +name = "tungstenite" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30ee6ab729cd4cf0fd55218530c4522ed30b7b6081752839b68fcec8d0960788" +dependencies = [ + "base64 0.13.0", + "byteorder", + "bytes", + "http", + "httparse", + "log", + "rand", + "sha1", + "thiserror", + "url", + "utf-8", +] + [[package]] name = "typenum" version = "1.15.0" @@ -660,22 +1361,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" [[package]] -name = "unicode-bidi" -version = "0.3.5" +name = "unicase" +version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eeb8be209bb1c96b7c177c7420d26e04eccacb0eeae6b980e35fcb74678107e0" -dependencies = [ - "matches", -] +checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" [[package]] -name = "unicode-normalization" -version = "0.1.18" +name = "unicode-ident" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33717dca7ac877f497014e10d73f3acf948c342bee31b5ca7892faf94ccc6b49" -dependencies = [ - "tinyvec", -] +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" [[package]] name = "unicode-xid" @@ -685,13 +1380,12 @@ checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" [[package]] name = "url" -version = "2.2.2" +version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" dependencies = [ "form_urlencoded", "idna", - "matches", "percent-encoding", ] @@ -701,11 +1395,63 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + [[package]] name = "version_check" -version = "0.9.3" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "warp" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" +checksum = "ba431ef570df1287f7f8b07e376491ad54f84d26ac473489427231e1718e1f69" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "headers", + "http", + "hyper", + "log", + "mime", + "mime_guess", + "multer", + "percent-encoding", + "pin-project", + "rustls-pemfile", + "scoped-tls", + "serde", + "serde_json", + "serde_urlencoded", + "tokio", + "tokio-stream", + "tokio-tungstenite 0.18.0", + "tokio-util", + "tower-service", + "tracing", +] [[package]] name = "wasi" @@ -719,6 +1465,64 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasm-bindgen" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn 2.0.101", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] + [[package]] name = "winapi" version = "0.3.9" @@ -750,62 +1554,320 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-core" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "windows-interface" +version = "0.59.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "windows-link" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" + +[[package]] +name = "windows-result" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" +dependencies = [ + "windows-link", +] + [[package]] name = "windows-sys" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" dependencies = [ - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_msvc", + "windows_aarch64_msvc 0.36.1", + "windows_i686_gnu 0.36.1", + "windows_i686_msvc 0.36.1", + "windows_x86_64_gnu 0.36.1", + "windows_x86_64_msvc 0.36.1", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc 0.42.2", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + [[package]] name = "windows_aarch64_msvc" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + [[package]] name = "windows_i686_gnu" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + [[package]] name = "windows_i686_msvc" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + [[package]] name = "windows_x86_64_gnu" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + [[package]] name = "windows_x86_64_msvc" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "worker" +version = "0.1.0" +dependencies = [ + "futures", + "futures-util", + "rusqlite", + "serde", + "serde_json", + "shared", + "tokio", + "tokio-tungstenite 0.17.2", +] + +[[package]] +name = "writeable" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" + +[[package]] +name = "yoke" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", + "synstructure", +] + +[[package]] +name = "youoke-player" +version = "0.1.0" +dependencies = [ + "futures", + "serde", + "serde_json", + "tokio", + "warp", +] + [[package]] name = "youoke-server" version = "0.1.0" dependencies = [ "env_logger", + "futures", "futures-channel", "futures-util", "glob", "log", + "rusqlite", "serde", "serde_json", + "shared", "tokio", - "tokio-tungstenite", - "tungstenite", + "tokio-tungstenite 0.17.2", + "tungstenite 0.17.3", "url", + "warp", +] + +[[package]] +name = "zerocopy" +version = "0.8.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", + "synstructure", +] + +[[package]] +name = "zerotrie" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", ] diff --git a/Cargo.toml b/Cargo.toml index bf282d4..8a0f6eb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,4 @@ [workspace] # note: this is here at the top-level to appease vscode convention. -members = [ - "./server/src/..", -] +members = ["server", "player", "worker", "shared"] diff --git a/makefile b/makefile new file mode 100644 index 0000000..7b7c976 --- /dev/null +++ b/makefile @@ -0,0 +1,60 @@ +# === Variables === +CARGO := cargo +SERVER := server +WORKER := worker +DB := queue.db + +# === Help (default target) === +.PHONY: help +help: + @echo "๐Ÿ“ฆ Usage: make [target]" + @echo "" + @echo "๐Ÿ› ๏ธ Build & Run:" + @echo " make build - Build all workspace binaries" + @echo " make run-server - Run the WebSocket/HTTP server" + @echo " make run-worker - Run the job worker processor" + @echo "" + @echo "๐Ÿงน Dev Utilities:" + @echo " make fmt - Format code using rustfmt" + @echo " make lint - Lint all targets with clippy" + @echo " make clean - Clean target artifacts" + @echo " make reset-db - Delete SQLite database (queue.db)" + @echo "" + +# Default to help if no target is specified +.DEFAULT_GOAL := help + +# === Build All Binaries === +.PHONY: build +build: + $(CARGO) build --workspace + +# === Run Server === +.PHONY: run-server +run-server: + $(CARGO) run --bin $(SERVER) + +# === Run Worker === +.PHONY: run-worker +run-worker: + $(CARGO) run --bin $(WORKER) + +# === Format Code === +.PHONY: fmt +fmt: + $(CARGO) fmt --all + +# === Lint === +.PHONY: lint +lint: + $(CARGO) clippy --all-targets --all-features -- -D warnings + +# === Clean === +.PHONY: clean +clean: + $(CARGO) clean + +# === Reset DB === +.PHONY: reset-db +reset-db: + rm -f $(DB) diff --git a/server/Cargo.toml b/server/Cargo.toml index 090bab0..b4aabb7 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -16,5 +16,11 @@ log = "0.4.17" env_logger = "0.9.0" url = "2.2.2" serde_json = "1.0" -serde = {version = "1.0", features = ["derive"]} -glob = "0.3.0" +serde = { version = "1.0", features = ["derive"] } +glob = "0.3.2" +# v2 stuff +futures = "0.3" +warp = "0.3" +rusqlite = { version = "0.30", features = ["bundled"] } + +shared = { path = "../shared" } diff --git a/server/examples/sqlitedb.rs b/server/examples/sqlitedb.rs new file mode 100644 index 0000000..17d66fb --- /dev/null +++ b/server/examples/sqlitedb.rs @@ -0,0 +1,28 @@ +use rusqlite::Connection; +use serde_json::Value; +use shared::{db, db_worker::DbWorker, models::Job}; +use std::sync::Arc; +use warp::Filter; + +// use crate::db_worker::DbWorker; + +#[tokio::main] +async fn main() { + let db = DbWorker::start("queue.db"); + let db_filter = warp::any().map(move || db.clone()); + + let ws_route = warp::path("submit") + .and(warp::body::json()) + .and(db_filter) + .and_then(handle_submit); + + println!("๐Ÿš€ Server running on http://localhost:3030"); + warp::serve(ws_route).run(([127, 0, 0, 1], 3030)).await; +} + +pub async fn handle_submit(json: Value, db: DbWorker) -> Result { + let payload = serde_json::to_string(&json).unwrap(); + + db.enque("chat", &payload); // enqueue is a synchronous send() + Ok(warp::reply::json(&"queued")) +} diff --git a/server/examples/v2.rs b/server/examples/v2.rs new file mode 100644 index 0000000..1d10364 --- /dev/null +++ b/server/examples/v2.rs @@ -0,0 +1,192 @@ +// add to Cargo.toml these: +// [dependencies] +// tokio = { version = "1", features = ["full"] } +// warp = "0.3" +// serde = { version = "1", features = ["derive"] } +// serde_json = "1" +// futures = "0.3" +// web-view = "0.7" + +// main.rs +use futures::{SinkExt, StreamExt}; +use serde::{Deserialize, Serialize}; +use std::{convert::Infallible, process::Command, sync::Arc}; +use tokio::sync::Mutex; +use warp::ws::{Message, WebSocket}; +use warp::Filter; + +#[derive(Debug, Serialize, Deserialize)] +#[serde(tag = "type")] +enum CommandMessage { + ReadFile { path: String }, + WriteFile { path: String, data: String }, + SpawnProcess { command: String }, + ForwardToPlayer { action: String }, + Sleep { seconds: u64 }, +} + +#[derive(Default)] +struct AppState { + player_channel: Option>, +} + +#[derive(Debug)] +struct Unauthorized; + +impl warp::reject::Reject for Unauthorized {} + +type SharedState = Arc>; + +const SECRET_CODE: &str = "aaahhh"; + +#[tokio::main] +async fn main() { + let state: SharedState = Arc::new(Mutex::new(AppState::default())); + let (player_tx, mut player_rx) = tokio::sync::mpsc::channel(32); + state.lock().await.player_channel = Some(player_tx); + + // Start the central "player" task + tokio::spawn(async move { + while let Some(msg) = player_rx.recv().await { + println!("[Player] Received action: {}", msg); + } + }); + + let state_filter = warp::any().map(move || state.clone()); + + let ws_route = warp::path("ws") + .and(warp::ws()) + .and(warp::query::>()) // extract query params + .and(state_filter.clone()) + .and_then(handle_ws_request); + // .map(|ws: warp::ws::Ws, state| { + // ws.on_upgrade(move |socket| handle_connection(socket, state)) + // }); + + let static_files = warp::fs::dir("./library"); + + let routes = ws_route.or(static_files); + + println!("websocket server running at ws://localhost:3030/ws"); + println!("serving ./library at http://localhost:3030/"); + + // tokio::spawn(spawn_webview()); + warp::serve(routes).run(([127, 0, 0, 1], 3030)).await; +} + +// pub async fn handle_submit(json: Value, db: DbWorker) -> Result { + +async fn handle_ws_request( + ws: warp::ws::Ws, + query: std::collections::HashMap, + state: SharedState, +) -> Result { + if let Some(code) = query.get("code") { + if code == SECRET_CODE { + return Ok(ws.on_upgrade(move |socket| handle_connection(socket, state))); + } + } + + Err(warp::reject::custom(Unauthorized)) +} + +async fn handle_connection(ws: WebSocket, state: SharedState) { + let (mut tx, mut rx) = ws.split(); + + while let Some(result) = rx.next().await { + match result { + Ok(msg) if msg.is_text() => { + let msg_text = msg.to_str().unwrap_or(""); + if let Ok(command) = serde_json::from_str::(msg_text) { + let response = handle_command(command, state.clone()).await; + if let Some(resp) = response { + let _ = tx.send(Message::text(resp)).await; + } + } + } + _ => break, + } + } +} + +async fn handle_command(cmd: CommandMessage, state: SharedState) -> Option { + match cmd { + CommandMessage::ReadFile { path } => { + let contents = tokio::fs::read_to_string(path).await.ok()?; + Some(contents) + } + CommandMessage::WriteFile { path, data } => { + tokio::fs::write(path, data).await.ok()?; + Some("ok".to_string()) + } + CommandMessage::SpawnProcess { command } => { + tokio::spawn(async move { + let _ = Command::new("sh").arg("-c").arg(&command).spawn(); + }); + Some("spawned".to_string()) + } + CommandMessage::ForwardToPlayer { action } => { + let state = state.lock().await; + if let Some(sender) = &state.player_channel { + let _ = sender.send(action.clone()).await; + } + Some("forwarded".to_string()) + } + CommandMessage::Sleep { seconds } => { + tokio::time::sleep(std::time::Duration::from_secs(seconds)).await; + Some(format!("slept for {} seconds", seconds)) + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[tokio::test] + async fn test_sleep_command() { + let state = Arc::new(Mutex::new(AppState::default())); + let start = std::time::Instant::now(); + let result = handle_command(CommandMessage::Sleep { seconds: 1 }, state).await; + assert_eq!(result.unwrap(), "slept for 1 seconds"); + assert!(start.elapsed().as_secs() >= 1); + } + + #[tokio::test] + async fn test_write_and_read_file() { + let state = Arc::new(Mutex::new(AppState::default())); + let path = "testfile.txt".to_string(); + let data = "hello, world".to_string(); + + let write_result = handle_command( + CommandMessage::WriteFile { + path: path.clone(), + data: data.clone(), + }, + state.clone(), + ) + .await; + assert_eq!(write_result.unwrap(), "ok"); + + let read_result = handle_command(CommandMessage::ReadFile { path }, state.clone()).await; + assert_eq!(read_result.unwrap(), data); + } + + #[tokio::test] + async fn test_forward_to_player() { + let (tx, mut rx) = tokio::sync::mpsc::channel(1); + let mut state = AppState::default(); + state.player_channel = Some(tx); + let shared_state = Arc::new(Mutex::new(state)); + + let result = handle_command( + CommandMessage::ForwardToPlayer { + action: "test_action".to_string(), + }, + shared_state.clone(), + ) + .await; + assert_eq!(result.unwrap(), "forwarded"); + assert_eq!(rx.recv().await.unwrap(), "test_action"); + } +} diff --git a/shared/Cargo.toml b/shared/Cargo.toml new file mode 100644 index 0000000..59889af --- /dev/null +++ b/shared/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "shared" +version = "0.1.0" +edition = "2021" + +[dependencies] +serde = { version = "1", features = ["derive"] } +serde_json = "1" +rusqlite = { version = "0.30", features = ["bundled"] } +thiserror = "1.0" +chrono = "0.4" diff --git a/shared/src/db.rs b/shared/src/db.rs new file mode 100644 index 0000000..6d5d49c --- /dev/null +++ b/shared/src/db.rs @@ -0,0 +1,52 @@ +use crate::models::Job; +use chrono::Utc; +use rusqlite::{params, Connection}; + +pub fn init_db(conn: &Connection) { + conn.execute_batch( + "CREATE TABLE IF NOT EXISTS job_queue ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + status TEXT NOT NULL, + job_type TEXT NOT NULL, + payload TEXT NOT NULL, + created_at TEXT, + updated_at TEXT + );", + ) + .unwrap(); +} + +pub fn enqueue(conn: &Connection, job_type: &str, payload: &str) { + let now = Utc::now().to_rfc3339(); + conn.execute( + "INSERT INTO job_queue (status, job_type, payload, created_at, updated_at) VALUES ('pending', ?1, ?2, ?3, ?3)", + params![job_type, payload, now], + ).unwrap(); +} + +pub fn fetch_pending(conn: &Connection, limit: usize) -> Vec { + let mut stmt = conn.prepare( + "SELECT id, status, job_type, payload FROM job_queue WHERE status = 'pending' ORDER BY created_at LIMIT ?1", + ).unwrap(); + + let rows = stmt + .query_map(params![limit], |row| { + Ok(Job { + id: row.get(0)?, + status: row.get(1)?, + job_type: row.get(2)?, + payload: row.get(3)?, + }) + }) + .unwrap(); + + rows.filter_map(Result::ok).collect() +} + +pub fn update_status(conn: &Connection, id: i64, status: &str) { + conn.execute( + "UPDATE job_queue SET status = ?1, updated_at = ?2 WHERE id = ?3", + params![status, Utc::now().to_rfc3339(), id], + ) + .unwrap(); +} diff --git a/shared/src/db_worker.rs b/shared/src/db_worker.rs new file mode 100644 index 0000000..2a1ba36 --- /dev/null +++ b/shared/src/db_worker.rs @@ -0,0 +1,78 @@ +use rusqlite::Connection; +use std::sync::mpsc; +use std::thread; + +use crate::db; +use crate::models::Job; + +#[derive(Clone)] +pub struct DbWorker { + tx: mpsc::Sender, +} + +enum DbCommand { + FetchPending { + limit: usize, + respond_to: mpsc::Sender>, + }, + UpdateStatus { + id: i64, + status: String, + }, + Enque { + job_type: String, + payload: String, + }, +} + +impl DbWorker { + pub fn start(db_path: &str) -> Self { + let (tx, rx) = mpsc::channel::(); + let path = db_path.to_string(); + + thread::spawn(move || { + let conn = Connection::open(path).unwrap(); + db::init_db(&conn); + + for cmd in rx { + match cmd { + DbCommand::FetchPending { limit, respond_to } => { + let jobs = db::fetch_pending(&conn, limit); + let _ = respond_to.send(jobs); + } + DbCommand::UpdateStatus { id, status } => { + db::update_status(&conn, id, &status); + } + DbCommand::Enque { job_type, payload } => { + db::enqueue(&conn, &job_type, &payload); + } + } + } + }); + + DbWorker { tx } + } + + pub fn fetch_pending(&self, limit: usize) -> Vec { + let (tx, rx) = mpsc::channel(); + let _ = self.tx.send(DbCommand::FetchPending { + limit, + respond_to: tx, + }); + rx.recv().unwrap_or_else(|_| vec![]) + } + + pub fn update_status(&self, id: i64, status: &str) { + let _ = self.tx.send(DbCommand::UpdateStatus { + id, + status: status.to_string(), + }); + } + + pub fn enque(&self, job_type: &str, status: &str) { + let _ = self.tx.send(DbCommand::Enque { + job_type: job_type.to_string(), + payload: status.to_string(), + }); + } +} diff --git a/shared/src/lib.rs b/shared/src/lib.rs new file mode 100644 index 0000000..ccab64d --- /dev/null +++ b/shared/src/lib.rs @@ -0,0 +1,3 @@ +pub mod db; +pub mod db_worker; +pub mod models; diff --git a/shared/src/models.rs b/shared/src/models.rs new file mode 100644 index 0000000..913dbf1 --- /dev/null +++ b/shared/src/models.rs @@ -0,0 +1,9 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Clone)] +pub struct Job { + pub id: i64, + pub job_type: String, + pub status: String, + pub payload: String, +} diff --git a/worker/Cargo.toml b/worker/Cargo.toml new file mode 100644 index 0000000..29a5489 --- /dev/null +++ b/worker/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "worker" +version = "0.1.0" +edition = "2021" + +[dependencies] +tokio = { version = "1", features = ["full"] } +tokio-tungstenite = "0.17.2" +futures = "0.3" +futures-util = "0.3.23" +serde = { version = "1", features = ["derive"] } +serde_json = "1" +rusqlite = { version = "0.30", features = ["bundled"] } + +shared = { path = "../shared" } diff --git a/worker/src/job_handlers.rs b/worker/src/job_handlers.rs new file mode 100644 index 0000000..28920b7 --- /dev/null +++ b/worker/src/job_handlers.rs @@ -0,0 +1,36 @@ +use shared::models::Job; +use tokio::time::{sleep, Duration}; + +pub async fn process_job(job: Job) -> Result<(), String> { + match job.job_type.as_str() { + "auth" => process_auth(job).await, + "chat" => process_chat(job).await, + "email" => process_email(job).await, + "fook" => process_fook(job).await, + other => Err(format!("Unknown job_type: {}", other)), + } +} + +async fn process_auth(job: Job) -> Result<(), String> { + println!("auth job {}: {}", job.id, job.payload); + sleep(Duration::from_secs(1)).await; + Ok(()) +} + +async fn process_chat(job: Job) -> Result<(), String> { + println!("chat job {}: {}", job.id, job.payload); + sleep(Duration::from_secs(2)).await; + Ok(()) +} + +async fn process_email(job: Job) -> Result<(), String> { + println!("email job {}: {}", job.id, job.payload); + sleep(Duration::from_secs(3)).await; + Ok(()) +} + +async fn process_fook(job: Job) -> Result<(), String> { + println!("fook job {}: {}", job.id, job.payload); + sleep(Duration::from_secs(3)).await; + Ok(()) +} diff --git a/worker/src/main.rs b/worker/src/main.rs new file mode 100644 index 0000000..ef38c52 --- /dev/null +++ b/worker/src/main.rs @@ -0,0 +1,37 @@ +mod job_handlers; + +use job_handlers::process_job; +use shared::db_worker::DbWorker; + +use tokio::time::sleep; + +#[tokio::main] +async fn main() { + let db = DbWorker::start("queue.db"); + + for _ in 0..4 { + let db = db.clone(); + tokio::spawn(async move { + loop { + let jobs = db.fetch_pending(1); + for job in jobs { + db.update_status(job.id, "in_progress"); + + match process_job(job.clone()).await { + Ok(_) => db.update_status(job.id, "done"), + Err(e) => { + eprintln!("Job {} failed: {}", job.id, e); + db.update_status(job.id, "failed"); + } + } + } + + sleep(std::time::Duration::from_millis(300)).await; + } + }); + } + + loop { + sleep(std::time::Duration::from_secs(60)).await; + } +} From de110af487112c8edbfcf6ea25b9697c6b85f224 Mon Sep 17 00:00:00 2001 From: edward sharp Date: Tue, 3 Jun 2025 01:06:40 -0400 Subject: [PATCH 02/26] =?UTF-8?q?okay!=20room=20handshake=20code=20thing?= =?UTF-8?q?=20=F0=9F=A4=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/pages/Landing.tsx | 212 +++++++++++++++++++---------------- client/src/pages/Room.tsx | 3 +- server/src/main.rs | 67 ++++++++++- 3 files changed, 181 insertions(+), 101 deletions(-) diff --git a/client/src/pages/Landing.tsx b/client/src/pages/Landing.tsx index 99f6862..1d8f840 100644 --- a/client/src/pages/Landing.tsx +++ b/client/src/pages/Landing.tsx @@ -1,4 +1,4 @@ -import { useState } from 'react' +import { useEffect, useState } from 'react' import useInterval from '../hooks' import './Landing.css' @@ -11,16 +11,16 @@ export interface LandingProps { type RoomList = IRoom[] const KNOWN_ROOMS: RoomList = [ - { name: 'LOCALHOST', href: 'ws://localhost:9001' }, + { name: 'LOCALHOST', href: 'ws://localhost:9001', code: '' }, // { name: 'FOLK', href: 'ws://10.246.17.194:9001' }, - { name: 'PARTYLINE', href: 'wss://f8da-68-161-154-113.ngrok-free.app' }, + // { name: 'PIZZAPARTY', href: 'wss://youoke.ngrok.pizza' }, ] -function testWS(href: string): Promise { - const ws = new WebSocket(href) +function testWS(href: string, code: string): Promise { + const ws = new WebSocket(`${href}?code=${code}`) return new Promise((resolve, reject) => { - ws.onerror = () => reject(false) + ws.onerror = (e) => reject(e) ws.onopen = () => { ws.close() resolve(true) @@ -31,23 +31,30 @@ function testWS(href: string): Promise { export default function Landing(props: LandingProps) { const { setRoom } = props + const [code, setCode] = useState('') const [addNewRoom, setAddNewRoom] = useState(false) const [newRoom, setNewRoom] = useState(KNOWN_ROOMS[0]) const [roomsToFind, setRoomsToFind] = useState(KNOWN_ROOMS) const [roomList, setRoomList] = useState() - const [delay, setDelay] = useState(1000) + const [delay, setDelay] = useState(null) + + useEffect(() => { + if (code && delay === null) { + setDelay(1000) + } + }, [code, delay]) useInterval( () => { - if (roomsToFind.length === 0) { + if (roomsToFind.length === 0 || !code) { setDelay(null) return } roomsToFind.forEach((room) => { - testWS(room.href) + testWS(room.href, code) .then(() => { console.log('zomg FOUND room!', room) - setRoomList((prev) => [...(prev ? prev : []), room]) + setRoomList((prev) => [...(prev ? prev : []), { ...room, code }]) const roomsToFindClone = [...roomsToFind] const idx = roomsToFindClone.indexOf(room) if (idx > -1) { @@ -55,8 +62,8 @@ export default function Landing(props: LandingProps) { setRoomsToFind(roomsToFindClone) } }) - .catch(() => { - console.warn('onoz, bad room!', room) + .catch((e) => { + console.warn('onoz, bad room!', room, ' error:', e) }) }) @@ -72,93 +79,110 @@ export default function Landing(props: LandingProps) {

YOUOKE

- - - JOIN ROOM - - -

-
    -
  1. !addNewRoom && setAddNewRoom(true)} - > - {addNewRoom ? ( - <> - - - -
    -
    { - setRoomsToFind((prev) => { - if (prev.find((r) => r.name === newRoom.name && r.href === newRoom.href)) { - return prev - } - - return [...prev, newRoom] - }) - // reset inputz? - // setNewRoom(KNOWN_ROOMS[0]) - setAddNewRoom(false) - }} - > - add new room + + {code && ( +
      +
    1. !addNewRoom && setAddNewRoom(true)} + > + {addNewRoom ? ( + <> + + + +
      +
      { + setRoomsToFind((prev) => { + if ( + prev.find( + (r) => + r.name === newRoom.name && + r.href === newRoom.href + ) + ) { + return prev + } + + return [...prev, newRoom] + }) + // reset inputz? + // setNewRoom(KNOWN_ROOMS[0]) + setAddNewRoom(false) + }} + > + add new room +
      + +
      { + setAddNewRoom(false) + }} + > + x +
      + + ) : ( + 'find room...' + )} +
    2. -
      ( +
    3. { - setAddNewRoom(false) + setRoom(room) + setDelay(null) }} > - x -
    4. -
    - - ) : ( - 'find room...' - )} -
  2. - - {!roomList - ? 'looking for rooms...' - : roomList.map((room, idx) => ( -
  3. { - setRoom(room) - setDelay(null) - }} - > - {room.name} -
  4. - ))} -
+ {room.name} + + ))} + + )}
) diff --git a/client/src/pages/Room.tsx b/client/src/pages/Room.tsx index 9a4938b..9599470 100644 --- a/client/src/pages/Room.tsx +++ b/client/src/pages/Room.tsx @@ -7,6 +7,7 @@ import youtubeSearch, { YTSearchItem } from '../youtube' export interface IRoom { name: string href: string + code: string } export interface RoomProps { @@ -171,7 +172,7 @@ export default function Room(props: RoomProps) { } useEffect(() => { - ws.current = new WebSocket(room.href) + ws.current = new WebSocket(`${room.href}?code=${room.code}`) ws.current.onopen = () => { setWsStatus('open') sendWsMessage('GetLibrary') diff --git a/server/src/main.rs b/server/src/main.rs index 22afa79..176f48a 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -2,11 +2,12 @@ use std::{ collections::HashMap, env, fs::{canonicalize, create_dir_all, read_to_string}, - io::Error as IoError, + io::{self, Error as IoError}, net::SocketAddr, path::{Path, PathBuf}, process::{Command, Stdio}, sync::{Arc, Mutex}, + time::{SystemTime, UNIX_EPOCH}, }; use glob::glob; @@ -15,8 +16,14 @@ use futures_channel::mpsc::{unbounded, UnboundedReceiver, UnboundedSender}; use futures_util::{future, pin_mut, stream::TryStreamExt, StreamExt}; use tokio::net::{TcpListener, TcpStream}; -use tokio_tungstenite::accept_async; +use tokio_tungstenite::accept_hdr_async; + +use tokio_tungstenite::tungstenite::handshake::server::{ + ErrorResponse as HandshakeErrorResponse, Request as HandshakeRequest, + Response as HandshakeResponse, +}; use tungstenite::protocol::Message; +// use url::Url; use log::*; use serde::{Deserialize, Serialize}; @@ -177,6 +184,14 @@ async fn main() -> Result<(), IoError> { q_sender.clone(), )); + println!("serving {} at http://localhost:9002/", &library_path); + tokio::task::spawn( + warp::serve(warp::fs::dir(library_path.clone())).run(([127, 0, 0, 1], 9002)), + ); + + let handshake_code = generate_code(); + println!("THE HANDSHAKE CODE IS: {}", &handshake_code); + // spawn the handling of each connection in a separate task. while let Ok((stream, addr)) = listener.accept().await { tokio::spawn(connection_handler( @@ -185,6 +200,7 @@ async fn main() -> Result<(), IoError> { addr, q_sender.clone(), library_path.clone(), + handshake_code.clone(), )); } @@ -197,11 +213,40 @@ async fn connection_handler( addr: SocketAddr, q_sender: UnboundedSender, library_path: String, + handshake_code: String, ) { info!("incoming TCP connection from: {}", addr); - let ws_stream = accept_async(raw_stream) - .await - .expect("error during the websocket handshake occurred"); + + // let check_handshake_code = |req: &HandshakeRequest, resp: HandshakeResponse| { + let check_handshake_code = move |req: &HandshakeRequest, + resp: HandshakeResponse| + -> Result { + info!("zomg gonna check_handshake_code"); + if let Some(uri) = req.uri().path_and_query() { + if let Ok(parsed_url) = url::Url::parse(&format!("http://localhost{}", uri)) { + if let Some(code) = parsed_url.query_pairs().find(|(k, _)| k == "code") { + info!("zomg checking code {}", code.1); + if code.1 == *handshake_code { + info!("zomg okay good handshake bro!"); + return Ok(resp); + } + } + } + } + + Err(HandshakeErrorResponse::new(Some( + "Unauthorized".to_string(), + ))) + }; + + // let ws_stream = accept_hdr_async(raw_stream, check_handshake_code).await?; + let ws_stream = match accept_hdr_async(raw_stream, check_handshake_code).await { + Ok(stream) => stream, + Err(err) => { + eprintln!("Handshake failed: {}", err); + return; // or future::ok(()) if youโ€™re returning a future + } + }; info!("WebSocket connection established: {}", addr); let (tx, rx) = unbounded(); // insert the write (tx) part of this peer to the peer map @@ -542,7 +587,7 @@ async fn download_handler( .arg(&id) // note: this handles video IDz that start with a dash (-) .output() // note: sleep for debuggin. - // let response: Request = match Command::new("sleep").arg("1").output() + // let response: Request = match Command::new("sleep").arg("1").output() { Ok(output) => { info!("download_handler yt-dlp output: {:#?}", output); @@ -610,6 +655,16 @@ async fn download_handler( Ok(()) } +fn generate_code() -> String { + let nanos = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .subsec_nanos(); + + let code = (nanos % 1_000_000).to_string(); + format!("{:0>6}", code) // zero-pad to 6 digits +} + #[cfg(test)] mod tests { use super::*; From a7a68185d1f696516a0194744615943672a6d7dc Mon Sep 17 00:00:00 2001 From: edward sharp Date: Tue, 3 Jun 2025 10:59:25 -0400 Subject: [PATCH 03/26] =?UTF-8?q?okay,=20better=20handshake=20code=20handl?= =?UTF-8?q?ing=20=F0=9F=A4=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/App.tsx | 10 +- client/src/pages/Landing.css | 5 + client/src/pages/Landing.tsx | 244 ++++++++++++++++++----------------- client/src/pages/Room.tsx | 3 +- server/examples/v2.rs | 5 +- server/src/main.rs | 29 ++++- 6 files changed, 164 insertions(+), 132 deletions(-) diff --git a/client/src/App.tsx b/client/src/App.tsx index e9a4dbb..4bea2b0 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -10,10 +10,14 @@ function App() { const search = window.location.search const params = new URLSearchParams(search) const name = params.get('name') - const href = params.get('href') - if (name && href) { + let href = params.get('href') + const code = params.get('code') + if (name && href && code) { console.log('zomg have room from query params!', { name, href }) - return { name, href } + if (!href.startsWith('ws://') || !href.startsWith('wss://')) { + href = `ws://${href}` + } + return { name, href, code } } // otherwise fallback to localstorage diff --git a/client/src/pages/Landing.css b/client/src/pages/Landing.css index 10faf52..1dba91a 100644 --- a/client/src/pages/Landing.css +++ b/client/src/pages/Landing.css @@ -24,6 +24,11 @@ cursor: pointer; } +.code { + display: flex; + gap: 1em; +} + @media (max-width: 500px) { .youoke { font-size: 3em; diff --git a/client/src/pages/Landing.tsx b/client/src/pages/Landing.tsx index 1d8f840..9a63451 100644 --- a/client/src/pages/Landing.tsx +++ b/client/src/pages/Landing.tsx @@ -1,4 +1,4 @@ -import { useEffect, useState } from 'react' +import { useState } from 'react' import useInterval from '../hooks' import './Landing.css' @@ -16,45 +16,37 @@ const KNOWN_ROOMS: RoomList = [ // { name: 'PIZZAPARTY', href: 'wss://youoke.ngrok.pizza' }, ] -function testWS(href: string, code: string): Promise { - const ws = new WebSocket(`${href}?code=${code}`) - - return new Promise((resolve, reject) => { - ws.onerror = (e) => reject(e) - ws.onopen = () => { - ws.close() - resolve(true) - } - }) +function testWS(href: string): Promise { + // simple ping to see if server is alive + return fetch( + `${href.replace('ws://', 'http://').replace('wss://', 'https://')}/hello` + ) + .then((response) => response.status === 200) + .catch(() => false) } export default function Landing(props: LandingProps) { const { setRoom } = props const [code, setCode] = useState('') + const [needsCode, setNeedsCode] = useState(false) const [addNewRoom, setAddNewRoom] = useState(false) const [newRoom, setNewRoom] = useState(KNOWN_ROOMS[0]) const [roomsToFind, setRoomsToFind] = useState(KNOWN_ROOMS) const [roomList, setRoomList] = useState() - const [delay, setDelay] = useState(null) - - useEffect(() => { - if (code && delay === null) { - setDelay(1000) - } - }, [code, delay]) + const [delay, setDelay] = useState(1000) useInterval( () => { - if (roomsToFind.length === 0 || !code) { + if (roomsToFind.length === 0) { setDelay(null) return } roomsToFind.forEach((room) => { - testWS(room.href, code) + testWS(room.href) .then(() => { console.log('zomg FOUND room!', room) - setRoomList((prev) => [...(prev ? prev : []), { ...room, code }]) + setRoomList((prev) => [...(prev ? prev : []), room]) const roomsToFindClone = [...roomsToFind] const idx = roomsToFindClone.indexOf(room) if (idx > -1) { @@ -62,8 +54,9 @@ export default function Landing(props: LandingProps) { setRoomsToFind(roomsToFindClone) } }) - .catch((e) => { - console.warn('onoz, bad room!', room, ' error:', e) + .catch(() => { + // console.warn('onoz, bad room!', room, ' error:', e) + // ๐Ÿคทโ€โ™€๏ธ }) }) @@ -79,110 +72,119 @@ export default function Landing(props: LandingProps) {

YOUOKE

- - - JOIN ROOM - - -

- - {code && ( -
    -
  1. !addNewRoom && setAddNewRoom(true)} - > - {addNewRoom ? ( - <> - - - -
    -
    { - setRoomsToFind((prev) => { - if ( - prev.find( - (r) => - r.name === newRoom.name && - r.href === newRoom.href - ) - ) { - return prev - } - - return [...prev, newRoom] - }) - // reset inputz? - // setNewRoom(KNOWN_ROOMS[0]) - setAddNewRoom(false) - }} - > - add new room -
    -
    { - setAddNewRoom(false) - }} - > - x -
    +
      +
    1. !addNewRoom && setAddNewRoom(true)} + > + {addNewRoom ? ( + <> + + + +
      +
      { + setRoomsToFind((prev) => { + if ( + prev.find( + (r) => + r.name === newRoom.name && r.href === newRoom.href + ) + ) { + return prev + } + + return [...prev, newRoom] + }) + // reset inputz? + // setNewRoom(KNOWN_ROOMS[0]) + setAddNewRoom(false) + }} + > + add new room
      - - ) : ( - 'find room...' - )} -
    2. - {!roomList - ? 'looking for rooms...' - : roomList.map((room, idx) => ( -
    3. { - setRoom(room) - setDelay(null) + setAddNewRoom(false) }} > - {room.name} -
    4. - ))} -
    - )} + x +
    +
+ + ) : ( + 'find room...' + )} + + + {!roomList + ? 'looking for rooms...' + : roomList.map((room, idx) => ( +
  • { + setNeedsCode(true) + }} + > + {needsCode ? ( + + ) : ( + room.name + )} +
  • + ))} + ) diff --git a/client/src/pages/Room.tsx b/client/src/pages/Room.tsx index 9599470..a82a5d7 100644 --- a/client/src/pages/Room.tsx +++ b/client/src/pages/Room.tsx @@ -172,6 +172,7 @@ export default function Room(props: RoomProps) { } useEffect(() => { + // console.log('zomg using ws url:', `${room.href}?code=${room.code}`) ws.current = new WebSocket(`${room.href}?code=${room.code}`) ws.current.onopen = () => { setWsStatus('open') @@ -183,7 +184,7 @@ export default function Room(props: RoomProps) { return () => { ws.current && ws.current.close() } - }, [room.href]) + }, [room.href, room.code]) useEffect(() => { const fResults = library.filter((item) => diff --git a/server/examples/v2.rs b/server/examples/v2.rs index 1d10364..6c04030 100644 --- a/server/examples/v2.rs +++ b/server/examples/v2.rs @@ -63,9 +63,12 @@ async fn main() { // ws.on_upgrade(move |socket| handle_connection(socket, state)) // }); + let hello_route = warp::path("hello") + .and(warp::get()) + .map(|| warp::reply::with_status("hello!", warp::http::StatusCode::OK)); let static_files = warp::fs::dir("./library"); - let routes = ws_route.or(static_files); + let routes = ws_route.or(static_files).or(hello_route); println!("websocket server running at ws://localhost:3030/ws"); println!("serving ./library at http://localhost:3030/"); diff --git a/server/src/main.rs b/server/src/main.rs index 176f48a..4628cab 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -23,7 +23,8 @@ use tokio_tungstenite::tungstenite::handshake::server::{ Response as HandshakeResponse, }; use tungstenite::protocol::Message; -// use url::Url; + +use warp::Filter; use log::*; use serde::{Deserialize, Serialize}; @@ -184,14 +185,30 @@ async fn main() -> Result<(), IoError> { q_sender.clone(), )); - println!("serving {} at http://localhost:9002/", &library_path); - tokio::task::spawn( - warp::serve(warp::fs::dir(library_path.clone())).run(([127, 0, 0, 1], 9002)), + println!( + "serving /hello and library({}) at http://localhost:9002", + &library_path ); + let hello_route = warp::path("hello") + .and(warp::get()) + .map(|| warp::reply::with_status("hello!", warp::http::StatusCode::OK)); + let static_files = warp::fs::dir("./library"); - let handshake_code = generate_code(); - println!("THE HANDSHAKE CODE IS: {}", &handshake_code); + let routes = hello_route.or(static_files); + tokio::task::spawn(warp::serve(routes).run(([127, 0, 0, 1], 9002))); + // #todo: use match env::var_os("HANDSHAKE_CODE") or something + let handshake_code = generate_code(); + println!(""); + println!("- - - - - - - - - -"); + println!("-> HANDSHAKE CODE"); + println!("-> {}", &handshake_code); + println!( + "-> http://localhost:3000?href={}&name={}&code={}", + "localhost%3A9001", "localdev", &handshake_code + ); + println!("- - - - - - - - - -"); + println!(""); // spawn the handling of each connection in a separate task. while let Ok((stream, addr)) = listener.accept().await { tokio::spawn(connection_handler( From 72aaafe258dc6c1649b0497868d76fce06150f52 Mon Sep 17 00:00:00 2001 From: edward sharp Date: Tue, 3 Jun 2025 12:34:01 -0400 Subject: [PATCH 04/26] check code before trying to start ws :feelsgood: --- client/src/pages/Landing.tsx | 24 ++++++++++++++--- server/src/main.rs | 52 +++++++++++++++++++++++++++++------- 2 files changed, 63 insertions(+), 13 deletions(-) diff --git a/client/src/pages/Landing.tsx b/client/src/pages/Landing.tsx index 9a63451..6e10c92 100644 --- a/client/src/pages/Landing.tsx +++ b/client/src/pages/Landing.tsx @@ -16,7 +16,7 @@ const KNOWN_ROOMS: RoomList = [ // { name: 'PIZZAPARTY', href: 'wss://youoke.ngrok.pizza' }, ] -function testWS(href: string): Promise { +function testRoom(href: string): Promise { // simple ping to see if server is alive return fetch( `${href.replace('ws://', 'http://').replace('wss://', 'https://')}/hello` @@ -25,6 +25,17 @@ function testWS(href: string): Promise { .catch(() => false) } +function testCode(href: string, code: string): Promise { + // simple ping to see if server is alive + const fixed_href = href + .replace('ws://', 'http://') + .replace('wss://', 'https://') + .replace('9001', '9002') + return fetch(`${fixed_href}/hello?code=${code}`) + .then((response) => response.status === 200) + .catch(() => false) +} + export default function Landing(props: LandingProps) { const { setRoom } = props @@ -43,7 +54,7 @@ export default function Landing(props: LandingProps) { return } roomsToFind.forEach((room) => { - testWS(room.href) + testRoom(room.href) .then(() => { console.log('zomg FOUND room!', room) setRoomList((prev) => [...(prev ? prev : []), room]) @@ -169,8 +180,13 @@ export default function Landing(props: LandingProps) { const c = e.target.value setCode(c) if (c.length > 5) { - setRoom({ ...room, code: c }) - setDelay(null) + testCode(room.href, c) + .then((success) => { + if (!success) return + setRoom({ ...room, code: c }) + setDelay(null) + }) + .catch(() => {}) } }} value={code} diff --git a/server/src/main.rs b/server/src/main.rs index 4628cab..afa6d6a 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -24,6 +24,8 @@ use tokio_tungstenite::tungstenite::handshake::server::{ }; use tungstenite::protocol::Message; +use warp::cors; +use warp::http::StatusCode; use warp::Filter; use log::*; @@ -153,6 +155,13 @@ async fn main() -> Result<(), IoError> { Some(val) => val.into_string().unwrap(), None => "127.0.0.1:9001".to_string(), }; + + let http_address = match env::var_os("HTTP_ADDRESS") { + Some(val) => val.into_string().unwrap(), + None => "127.0.0.1:9002".to_string(), + }; + let http_addr: SocketAddr = http_address.parse().expect("Invalid HTTP_ADDRESS"); + let peer_map = PeerMap::new(Mutex::new(HashMap::new())); let queue: Vec = vec![]; @@ -186,23 +195,48 @@ async fn main() -> Result<(), IoError> { )); println!( - "serving /hello and library({}) at http://localhost:9002", - &library_path + "serving /hello and library({}) at http://{}", + &library_path, &http_addr ); + + // warp server setup stuff + let cors = cors() + .allow_any_origin() + .allow_methods(vec!["GET", "POST", "OPTIONS"]) // Add methods you need + .allow_headers(vec!["Content-Type"]); // Optional: allow custom headers + + // #todo: use match env::var_os("HANDSHAKE_CODE") or something + let handshake_code = Arc::new(generate_code()); + + let code_filter = warp::any().map({ + let secret_code = handshake_code.clone(); // Clone into filter + move || secret_code.clone() + }); + let hello_route = warp::path("hello") .and(warp::get()) - .map(|| warp::reply::with_status("hello!", warp::http::StatusCode::OK)); - let static_files = warp::fs::dir("./library"); + .and(warp::query::>()) + .and(code_filter) + .map( + |query: HashMap, secret_code: Arc| match query.get("code") { + Some(code) if code == secret_code.as_str() => { + warp::reply::with_status("hello!", StatusCode::OK) + } + _ => warp::reply::with_status("unauthorized", StatusCode::UNAUTHORIZED), + }, + ) + .with(&cors); + + let static_files = warp::fs::dir("./library").with(&cors); let routes = hello_route.or(static_files); - tokio::task::spawn(warp::serve(routes).run(([127, 0, 0, 1], 9002))); - // #todo: use match env::var_os("HANDSHAKE_CODE") or something - let handshake_code = generate_code(); + tokio::task::spawn(warp::serve(routes).run(http_addr)); + println!(""); println!("- - - - - - - - - -"); println!("-> HANDSHAKE CODE"); - println!("-> {}", &handshake_code); + println!("-> {}", handshake_code.as_str()); println!( "-> http://localhost:3000?href={}&name={}&code={}", "localhost%3A9001", "localdev", &handshake_code @@ -230,7 +264,7 @@ async fn connection_handler( addr: SocketAddr, q_sender: UnboundedSender, library_path: String, - handshake_code: String, + handshake_code: Arc, ) { info!("incoming TCP connection from: {}", addr); From 8b26134b91370cc1f8bc926dde72d5af02fd68cd Mon Sep 17 00:00:00 2001 From: edward sharp Date: Tue, 3 Jun 2025 13:01:50 -0400 Subject: [PATCH 05/26] =?UTF-8?q?search=20tip:=20add=20"karaoke"=20to=20se?= =?UTF-8?q?arch=20query=20=F0=9F=92=81=E2=80=8D=E2=99=80=EF=B8=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/pages/Room.css | 25 +++++++++++++++++++-- client/src/pages/Room.tsx | 46 +++++++++++++++++++++++++-------------- 2 files changed, 53 insertions(+), 18 deletions(-) diff --git a/client/src/pages/Room.css b/client/src/pages/Room.css index d15bda4..57ed33a 100644 --- a/client/src/pages/Room.css +++ b/client/src/pages/Room.css @@ -20,14 +20,27 @@ input { .flex-responsive { display: flex; } +.search-q { + width: 100%; +} .flex-grow { flex-grow: 1; } .search-q .list-btn { width: 30px; } -.search-q-input { - width: calc(100% - 60px); +.search-q .invert-list-btn { + height: 60px; + margin-top: 7px; +} +.search-q .flex-grow { + flex-grow: 1; +} + +.search-q-input-tip { + font-size: large; + line-height: 1em; + text-decoration: underline; } .search-results li { @@ -45,6 +58,14 @@ input { } @media (max-width: 500px) { + .search-q-input-tip { + font-size: xx-small; + line-height: 1em; + } + + .search-q .invert-list-btn { + margin-top: 0; + } #search-results-container { margin-left: -50px; } diff --git a/client/src/pages/Room.tsx b/client/src/pages/Room.tsx index a82a5d7..a4d43ef 100644 --- a/client/src/pages/Room.tsx +++ b/client/src/pages/Room.tsx @@ -188,7 +188,9 @@ export default function Room(props: RoomProps) { useEffect(() => { const fResults = library.filter((item) => - item.title.toLowerCase().includes(searchQ.toLowerCase()) + item.title + .toLowerCase() + .includes(searchQ.toLowerCase().replace('karaoke', '')) ) setSearchResults(fResults) document.getElementById('search-results-container')?.scrollIntoView() @@ -276,21 +278,33 @@ export default function Room(props: RoomProps) {
    {showSearchInput ? (
    - { - if (e.key === 'Escape') { - setShowSearchResults(false) - setShowSeachInout(false) - } - }} - onFocus={() => setShowSearchResults(true)} - value={searchQ} - onChange={(e) => setSearchQ(e.target.value)} - autoFocus - /> +
    + { + if (e.key === 'Escape') { + setShowSearchResults(false) + setShowSeachInout(false) + } + }} + onFocus={() => setShowSearchResults(true)} + value={searchQ} + onChange={(e) => setSearchQ(e.target.value)} + autoFocus + /> + {!searchQ.match('karaoke') && ( +
    + setSearchQ((prev) => `${prev} karaoke`) + } + > + tip: add "karaoke" to the search query! +
    + )} +
    {showSearchResults && (
    Date: Tue, 3 Jun 2025 15:41:54 -0400 Subject: [PATCH 06/26] move player to it's own thing, i guess :feelsgood: --- player/Cargo.toml | 38 ++++ player/README.md | 3 + .../public}/fonts/VCROSDMono.woff | Bin .../public}/fonts/VCROSDMono.woff2 | Bin .../public}/fonts/fonts.css | 0 .../player.html => player/public/index.html | 37 +++- player/public/youoke.png | Bin 0 -> 58720 bytes player/src/main.rs | 208 ++++++++++++++++++ server/src/main.rs | 49 ++++- 9 files changed, 317 insertions(+), 18 deletions(-) create mode 100644 player/Cargo.toml create mode 100644 player/README.md rename {server/examples => player/public}/fonts/VCROSDMono.woff (100%) rename {server/examples => player/public}/fonts/VCROSDMono.woff2 (100%) rename {server/examples => player/public}/fonts/fonts.css (100%) rename server/examples/player.html => player/public/index.html (84%) create mode 100644 player/public/youoke.png create mode 100644 player/src/main.rs diff --git a/player/Cargo.toml b/player/Cargo.toml new file mode 100644 index 0000000..a4842ad --- /dev/null +++ b/player/Cargo.toml @@ -0,0 +1,38 @@ +[package] +name = "youoke-player" +version = "0.1.0" +authors = ["eeddee "] +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +# [dependencies] +# tungstenite = "0.17.3" +# tokio-tungstenite = "0.17.2" +# futures-channel = "0.3.23" +# futures-util = "0.3.23" +# tokio = { version = "1.20.1", features = ["full"] } +# log = "0.4.17" +# env_logger = "0.9.0" +# url = "2.2.2" +# serde_json = "1.0" +# serde = { version = "1.0", features = ["derive"] } +# glob = "0.3.0" +# # v2 stuff +# warp = "0.3" +# futures = "0.3" +# web-view = "0.7" + +# [build-dependencies] +# tauri-build = { version = "2", features = [] } + +[dependencies] +tokio = { version = "1", features = ["full"] } +warp = "0.3" +serde = { version = "1", features = ["derive"] } +serde_json = "1" +futures = "0.3" +# tauri = { version = "2", features = ["api-all"] } +# tauri = { version = "2", features = [] } +# tauri-plugin-opener = "2" +# glob = "0.3.2" diff --git a/player/README.md b/player/README.md new file mode 100644 index 0000000..e930997 --- /dev/null +++ b/player/README.md @@ -0,0 +1,3 @@ +# youoke-player + +`RUST_LOG=info cargo run` diff --git a/server/examples/fonts/VCROSDMono.woff b/player/public/fonts/VCROSDMono.woff similarity index 100% rename from server/examples/fonts/VCROSDMono.woff rename to player/public/fonts/VCROSDMono.woff diff --git a/server/examples/fonts/VCROSDMono.woff2 b/player/public/fonts/VCROSDMono.woff2 similarity index 100% rename from server/examples/fonts/VCROSDMono.woff2 rename to player/public/fonts/VCROSDMono.woff2 diff --git a/server/examples/fonts/fonts.css b/player/public/fonts/fonts.css similarity index 100% rename from server/examples/fonts/fonts.css rename to player/public/fonts/fonts.css diff --git a/server/examples/player.html b/player/public/index.html similarity index 84% rename from server/examples/player.html rename to player/public/index.html index 13c01bb..a7a4e91 100644 --- a/server/examples/player.html +++ b/player/public/index.html @@ -1,9 +1,9 @@ - + youoke.party player - + - +
    -
      + +
      + +
      youoke.party
      +
      +
      +
      -
      diff --git a/client/src/main.tsx b/client/src/main.tsx index 3d7150d..c4fc9bb 100644 --- a/client/src/main.tsx +++ b/client/src/main.tsx @@ -6,5 +6,5 @@ import './index.css' ReactDOM.createRoot(document.getElementById('root')!).render( - , + ) diff --git a/client/src/pages/Landing.tsx b/client/src/pages/Landing.tsx index ec86fe5..ccea544 100644 --- a/client/src/pages/Landing.tsx +++ b/client/src/pages/Landing.tsx @@ -11,14 +11,18 @@ export interface LandingProps { type RoomList = IRoom[] -const name = - window.location.hostname !== 'localhost' - ? window.location.hostname - : 'LOCALHOST' // all capz, cuz better -const href = `ws://${window.location.hostname}:9001` +const name = window.location.hostname.includes('youoke.party') + ? 'LOCALHOST' // all capz, cuz better + : window.location.hostname +const href = `ws://${name.toLowerCase()}:9001` + +const search = window.location.search +const params = new URLSearchParams(search) +const code = params.get('code') || '' + const KNOWN_ROOMS: RoomList = [ { name, href, code: '' }, - { name: 'PIZZAPARTY', href: 'wss://youoke.ngrok.pizza', code: '' }, + { name: 'PIZZAPARTY', href: 'wss://youoke.ngrok.pizza', code }, ] function testRoom(href: string): Promise { @@ -44,6 +48,7 @@ function testCode(href: string, code: string): Promise { export default function Landing(props: LandingProps) { const { room, setRoom } = props + const [isHttps, setIsHttps] = useState(false) const [code, setCode] = useState('') const [needsCode, setNeedsCode] = useState(false) const [addNewRoom, setAddNewRoom] = useState(false) @@ -52,6 +57,10 @@ export default function Landing(props: LandingProps) { const [roomList, setRoomList] = useState() const [delay, setDelay] = useState(1000) + useEffect(() => { + setIsHttps(window.location.protocol === 'https:') + }, []) + useEffect(() => { if (!room) return console.log('zomg add props room!', room) @@ -93,6 +102,23 @@ export default function Landing(props: LandingProps) { return (

      YOUOKE

      + {isHttps && ( + <> +

      important! you're using "https://"

      +

      + so you need to manually type the "http://" part of + "http://youoke.party" +

      +

      + ...which is anoying, yeah +

      +

      + (i wish there was a better way, but your browser doesn't think i + should do that for you, which is, in some ways, understandable, but + in other ways, infuriating ๐Ÿคท) +

      + + )}

      - - - JOIN ROOM - - -

      From 516cb505e18116e242211cff05436645f2c12535 Mon Sep 17 00:00:00 2001 From: edward sharp Date: Tue, 3 Jun 2025 22:55:28 -0400 Subject: [PATCH 12/26] okay better new room setup :metal: --- client/src/App.tsx | 21 ------- client/src/pages/Landing.tsx | 115 ++++++++++++++++++++++++++--------- 2 files changed, 87 insertions(+), 49 deletions(-) diff --git a/client/src/App.tsx b/client/src/App.tsx index f5d2b54..84f2a15 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -6,27 +6,6 @@ import Room, { IRoom } from './pages/Room' function App() { const [room, setAppRoom] = useState(() => { - // first, try to get a room from the url query params: - const search = window.location.search - const params = new URLSearchParams(search) - let name = params.get('name') - let href = params.get('href') - const code = params.get('code') - if (href && code) { - if (!name) - name = - href.replace('ws://', '').replace('wss://', '').replace(/:\d+/, '') || - '' - console.log('zomg have room from query params!', { name, href }) - if (!href.startsWith('ws://') || !href.startsWith('wss://')) { - href = `ws://${href}` - } - if (!href.match(/:\d+/)) { - href = `${href}:9001` - } - return { name, href, code } - } - // otherwise fallback to localstorage const lRoom = localStorage.getItem('room') console.log('lRoom:', lRoom) diff --git a/client/src/pages/Landing.tsx b/client/src/pages/Landing.tsx index ccea544..32f17d7 100644 --- a/client/src/pages/Landing.tsx +++ b/client/src/pages/Landing.tsx @@ -11,18 +11,58 @@ export interface LandingProps { type RoomList = IRoom[] -const name = window.location.hostname.includes('youoke.party') - ? 'LOCALHOST' // all capz, cuz better - : window.location.hostname -const href = `ws://${name.toLowerCase()}:9001` - const search = window.location.search const params = new URLSearchParams(search) const code = params.get('code') || '' +const tryToTidyNameOrHref = (name?: string | null, href?: string | null) => { + if (!name && !href) return + + if (!name && href) { + name = + href.replace('ws://', '').replace('wss://', '').replace(/:\d+/, '') || '' + } + + if (!href && name) href = name + + // still no href?! + if (!href || !name) return + + if (!href.startsWith('ws://') || !href.startsWith('wss://')) { + href = `ws://${href}` + } + if (!href.match(/:\d+/)) { + href = `${href}:9001` + } + + console.log('zomg tryToTidyNameOrHref', { name, href, code }) + + return { name, href, code } +} + +const getWindowLocationRoom = () => { + const name = window.location.hostname.includes('youoke.party') + ? 'LOCALHOST' // all capz, cuz better + : window.location.hostname + const href = `ws://${name.toLowerCase()}:9001` + return { name, href, code } +} + +const getQueryParamsRoom = () => + tryToTidyNameOrHref(params.get('name'), params.get('href')) + +const getSpecialRooms = () => { + const queryParamsRoom = getQueryParamsRoom() + if (!queryParamsRoom) { + return [getWindowLocationRoom()] + } else { + return [getWindowLocationRoom(), queryParamsRoom] + } +} + const KNOWN_ROOMS: RoomList = [ - { name, href, code: '' }, - { name: 'PIZZAPARTY', href: 'wss://youoke.ngrok.pizza', code }, + ...getSpecialRooms(), + // { name: 'PIZZAPARTY', href: 'wss://youoke.ngrok.pizza', code }, ] function testRoom(href: string): Promise { @@ -50,9 +90,13 @@ export default function Landing(props: LandingProps) { const [isHttps, setIsHttps] = useState(false) const [code, setCode] = useState('') - const [needsCode, setNeedsCode] = useState(false) + const [needsCode, setNeedsCode] = useState>({}) const [addNewRoom, setAddNewRoom] = useState(false) - const [newRoom, setNewRoom] = useState(KNOWN_ROOMS[0]) + const [newRoom, setNewRoom] = useState({ + name: '', + href: '', + code: '', + }) const [roomsToFind, setRoomsToFind] = useState(KNOWN_ROOMS) const [roomList, setRoomList] = useState() const [delay, setDelay] = useState(1000) @@ -144,7 +188,7 @@ export default function Landing(props: LandingProps) { placeholder="name" /> - */}
      { setRoomsToFind((prev) => { - if ( - prev.find( - (r) => - r.name === newRoom.name && r.href === newRoom.href - ) - ) { + const fixedNewRoom = tryToTidyNameOrHref( + newRoom.name, + newRoom.href + ) + if (!fixedNewRoom || !fixedNewRoom.href) return prev + if (prev.find((r) => r.href === fixedNewRoom.href)) { return prev } - - return [...prev, newRoom] + return [...prev, fixedNewRoom] }) // reset inputz? // setNewRoom(KNOWN_ROOMS[0]) setAddNewRoom(false) + setDelay(1000) }} > add new room @@ -206,10 +250,13 @@ export default function Landing(props: LandingProps) { key={`${room}${idx}`} tabIndex={idx} onClick={() => { - setNeedsCode(true) + setNeedsCode((prev) => ({ + ...prev, + [`${room}${idx}`]: true, + })) }} > - {needsCode ? ( + {needsCode[`${room}${idx}`] ? (