From d2ca9e23d32ed41150e7e0acef41afcff574648c Mon Sep 17 00:00:00 2001 From: trisua Date: Sun, 23 Mar 2025 12:31:48 -0400 Subject: [PATCH] add: icon resolver add: config "no_track" file list option add: rainbeam-shared -> tetratto-shared add: l10n --- Cargo.lock | 983 +++++++++--------- Cargo.toml | 4 +- crates/app/Cargo.toml | 17 +- crates/app/src/assets.rs | 145 ++- crates/app/src/langs/en-US.toml | 6 + crates/app/src/macros.rs | 59 +- crates/app/src/main.rs | 24 +- crates/app/src/public/css/style.css | 16 +- crates/app/src/public/html/auth/login.html | 2 +- crates/app/src/public/html/auth/register.html | 2 +- crates/app/src/public/html/macros.html | 28 +- crates/app/src/public/html/root.html | 7 + .../app/src/public/images/default-avatar.svg | 10 +- .../app/src/public/images/default-banner.svg | 10 +- crates/app/src/public/images/favicon.svg | 53 + crates/app/src/routes/assets.rs | 5 + crates/app/src/routes/mod.rs | 3 +- crates/app/src/routes/pages/auth.rs | 10 +- crates/app/src/routes/pages/misc.rs | 6 +- crates/{tetratto_core => core}/Cargo.toml | 12 +- crates/{tetratto_core => core}/LICENSE | 0 crates/{tetratto_core => core}/src/config.rs | 18 + .../src/database/auth.rs | 2 +- .../src/database/drivers/common.rs | 0 .../src/database/drivers/mod.rs | 0 .../src/database/drivers/postgres.rs | 11 +- .../src/database/drivers/sql/create_users.sql | 0 .../src/database/drivers/sqlite.rs | 6 +- .../src/database/mod.rs | 0 crates/{tetratto_core => core}/src/lib.rs | 0 .../{tetratto_core => core}/src/model/auth.rs | 8 +- .../{tetratto_core => core}/src/model/mod.rs | 0 crates/l10n/Cargo.toml | 12 + crates/l10n/src/lib.rs | 95 ++ crates/shared/Cargo.toml | 16 + crates/shared/src/hash.rs | 38 + crates/shared/src/lib.rs | 5 + crates/shared/src/snow.rs | 52 + crates/shared/src/time.rs | 23 + example/.gitignore | 2 + 40 files changed, 1107 insertions(+), 583 deletions(-) create mode 100644 crates/app/src/langs/en-US.toml create mode 100644 crates/app/src/public/images/favicon.svg rename crates/{tetratto_core => core}/Cargo.toml (52%) rename crates/{tetratto_core => core}/LICENSE (100%) rename crates/{tetratto_core => core}/src/config.rs (90%) rename crates/{tetratto_core => core}/src/database/auth.rs (99%) rename crates/{tetratto_core => core}/src/database/drivers/common.rs (100%) rename crates/{tetratto_core => core}/src/database/drivers/mod.rs (100%) rename crates/{tetratto_core => core}/src/database/drivers/postgres.rs (89%) rename crates/{tetratto_core => core}/src/database/drivers/sql/create_users.sql (100%) rename crates/{tetratto_core => core}/src/database/drivers/sqlite.rs (84%) rename crates/{tetratto_core => core}/src/database/mod.rs (100%) rename crates/{tetratto_core => core}/src/lib.rs (100%) rename crates/{tetratto_core => core}/src/model/auth.rs (92%) rename crates/{tetratto_core => core}/src/model/mod.rs (100%) create mode 100644 crates/l10n/Cargo.toml create mode 100644 crates/l10n/src/lib.rs create mode 100644 crates/shared/Cargo.toml create mode 100644 crates/shared/src/hash.rs create mode 100644 crates/shared/src/lib.rs create mode 100644 crates/shared/src/snow.rs create mode 100644 crates/shared/src/time.rs diff --git a/Cargo.lock b/Cargo.lock index f306508..43705ca 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -32,19 +32,6 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4aa90d7ce82d4be67b64039a3d588d38dbcc6736577de4a847025ce5b0c468d1" -[[package]] -name = "ammonia" -version = "4.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ab99eae5ee58501ab236beb6f20f6ca39be615267b014899c89b2f0bc18a459" -dependencies = [ - "html5ever", - "maplit", - "once_cell", - "tendril", - "url", -] - [[package]] name = "android-tzdata" version = "0.1.1" @@ -60,56 +47,6 @@ dependencies = [ "libc", ] -[[package]] -name = "anstream" -version = "0.6.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" -dependencies = [ - "anstyle", - "anstyle-parse", - "anstyle-query", - "anstyle-wincon", - "colorchoice", - "is_terminal_polyfill", - "utf8parse", -] - -[[package]] -name = "anstyle" -version = "1.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" - -[[package]] -name = "anstyle-parse" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" -dependencies = [ - "utf8parse", -] - -[[package]] -name = "anstyle-query" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" -dependencies = [ - "windows-sys 0.59.0", -] - -[[package]] -name = "anstyle-wincon" -version = "3.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" -dependencies = [ - "anstyle", - "once_cell", - "windows-sys 0.59.0", -] - [[package]] name = "anyhow" version = "1.0.97" @@ -150,6 +87,12 @@ dependencies = [ "syn", ] +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + [[package]] name = "autocfg" version = "1.4.0" @@ -281,7 +224,7 @@ dependencies = [ "miniz_oxide", "object", "rustc-demangle", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -312,30 +255,6 @@ dependencies = [ "tokio-postgres", ] -[[package]] -name = "bincode" -version = "1.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" -dependencies = [ - "serde", -] - -[[package]] -name = "bit-set" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" -dependencies = [ - "bit-vec", -] - -[[package]] -name = "bit-vec" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" - [[package]] name = "bit_field" version = "0.10.2" @@ -369,31 +288,6 @@ dependencies = [ "generic-array", ] -[[package]] -name = "bon" -version = "3.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65268237be94042665b92034f979c42d431d2fd998b49809543afe3e66abad1c" -dependencies = [ - "bon-macros", - "rustversion", -] - -[[package]] -name = "bon-macros" -version = "3.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "803c95b2ecf650eb10b5f87dda6b9f6a1b758cee53245e2b7b825c9b3803a443" -dependencies = [ - "darling", - "ident_case", - "prettyplease", - "proc-macro2", - "quote", - "rustversion", - "syn", -] - [[package]] name = "bstr" version = "1.11.3" @@ -440,15 +334,6 @@ version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" -[[package]] -name = "caseless" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b6fd507454086c8edfd769ca6ada439193cdb209c7681712ef6275cccbfe5d8" -dependencies = [ - "unicode-normalization", -] - [[package]] name = "cc" version = "1.2.16" @@ -512,78 +397,12 @@ dependencies = [ "phf_codegen", ] -[[package]] -name = "clap" -version = "4.5.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6088f3ae8c3608d19260cd7445411865a485688711b78b5be70d78cd96136f83" -dependencies = [ - "clap_builder", - "clap_derive", -] - -[[package]] -name = "clap_builder" -version = "4.5.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22a7ef7f676155edfb82daa97f99441f3ebf4a58d5e32f295a56259f1b6facc8" -dependencies = [ - "anstream", - "anstyle", - "clap_lex", - "strsim", - "terminal_size", -] - -[[package]] -name = "clap_derive" -version = "4.5.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "clap_lex" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" - [[package]] name = "color_quant" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" -[[package]] -name = "colorchoice" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" - -[[package]] -name = "comrak" -version = "0.35.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52602e10393cfaaf8accaf707f2da743dc22cbe700a343ff8dbc9e5e04bc6b74" -dependencies = [ - "bon", - "caseless", - "clap", - "entities", - "memchr", - "shell-words", - "slug", - "syntect", - "typed-arena", - "unicode_categories", - "xdg", -] - [[package]] name = "cookie" version = "0.18.1" @@ -595,6 +414,16 @@ dependencies = [ "version_check", ] +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.7" @@ -660,41 +489,6 @@ dependencies = [ "typenum", ] -[[package]] -name = "darling" -version = "0.20.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" -dependencies = [ - "darling_core", - "darling_macro", -] - -[[package]] -name = "darling_core" -version = "0.20.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "strsim", - "syn", -] - -[[package]] -name = "darling_macro" -version = "0.20.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" -dependencies = [ - "darling_core", - "quote", - "syn", -] - [[package]] name = "deranged" version = "0.4.0" @@ -747,12 +541,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "entities" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5320ae4c3782150d900b79807611a59a99fc9a1d61d686faafc24b93fc8d7ca" - [[package]] name = "equivalent" version = "1.0.2" @@ -802,16 +590,6 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" -[[package]] -name = "fancy-regex" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b95f7c0680e4142284cf8b22c14a476e87d61b004a3a0861872b32ef7ead40a2" -dependencies = [ - "bit-set", - "regex", -] - [[package]] name = "fastrand" version = "2.3.0" @@ -849,6 +627,21 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + [[package]] name = "form_urlencoded" version = "1.2.1" @@ -858,16 +651,6 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "futf" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df420e2e84819663797d1ec6544b13c5be84629e7bb00dc960d6917db2987843" -dependencies = [ - "mac", - "new_debug_unreachable", -] - [[package]] name = "futures-channel" version = "0.3.31" @@ -995,6 +778,25 @@ dependencies = [ "walkdir", ] +[[package]] +name = "h2" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5017294ff4bb30944501348f6f8e42e6ad28f42c8bbef7a74029aff064a4e3c2" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "half" version = "2.5.0" @@ -1044,20 +846,6 @@ dependencies = [ "digest", ] -[[package]] -name = "html5ever" -version = "0.27.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c13771afe0e6e846f1e67d038d4cb29998a6779f93c809212e4e9c32efd244d4" -dependencies = [ - "log", - "mac", - "markup5ever", - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "http" version = "1.3.1" @@ -1128,6 +916,7 @@ dependencies = [ "bytes", "futures-channel", "futures-util", + "h2", "http", "http-body", "httparse", @@ -1136,6 +925,40 @@ dependencies = [ "pin-project-lite", "smallvec", "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2" +dependencies = [ + "futures-util", + "http", + "hyper", + "hyper-util", + "rustls", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", +] + +[[package]] +name = "hyper-tls" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +dependencies = [ + "bytes", + "http-body-util", + "hyper", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", ] [[package]] @@ -1145,13 +968,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4" dependencies = [ "bytes", + "futures-channel", "futures-util", "http", "http-body", "hyper", "pin-project-lite", + "socket2", "tokio", "tower-service", + "tracing", ] [[package]] @@ -1295,12 +1121,6 @@ dependencies = [ "syn", ] -[[package]] -name = "ident_case" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" - [[package]] name = "idna" version = "1.0.3" @@ -1399,10 +1219,10 @@ dependencies = [ ] [[package]] -name = "is_terminal_polyfill" -version = "1.70.1" +name = "ipnet" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" +checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" [[package]] name = "itertools" @@ -1488,12 +1308,6 @@ dependencies = [ "vcpkg", ] -[[package]] -name = "linked-hash-map" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" - [[package]] name = "linux-raw-sys" version = "0.9.3" @@ -1531,32 +1345,6 @@ dependencies = [ "imgref", ] -[[package]] -name = "mac" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" - -[[package]] -name = "maplit" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" - -[[package]] -name = "markup5ever" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16ce3abbeba692c8b8441d036ef91aea6df8da2c6b6e21c7e14d3c18e526be45" -dependencies = [ - "log", - "phf", - "phf_codegen", - "string_cache", - "string_cache_codegen", - "tendril", -] - [[package]] name = "matchers" version = "0.1.0" @@ -1658,6 +1446,23 @@ dependencies = [ "version_check", ] +[[package]] +name = "native-tls" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + [[package]] name = "new_debug_unreachable" version = "1.0.6" @@ -1762,25 +1567,47 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d75b0bedcc4fe52caa0e03d9f1151a323e4aa5e2d78ba3580400cd3c9e2bc4bc" [[package]] -name = "onig" -version = "6.4.0" +name = "openssl" +version = "0.10.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c4b31c8722ad9171c6d77d3557db078cab2bd50afcc9d09c8b315c59df8ca4f" +checksum = "5e14130c6a98cd258fdcb0fb6d744152343ff729cbfcb28c656a9d12b999fbcd" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.9.0", + "cfg-if", + "foreign-types", "libc", "once_cell", - "onig_sys", + "openssl-macros", + "openssl-sys", ] [[package]] -name = "onig_sys" -version = "69.8.1" +name = "openssl-macros" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b829e3d7e9cc74c7e315ee8edb185bf4190da5acde74afd7fc59c35b1f086e7" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "openssl-probe" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" + +[[package]] +name = "openssl-sys" +version = "0.9.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bb61ea9811cc39e3c2069f40b8b8e2e70d8569b361f879786cc7ed48b777cdd" dependencies = [ "cc", + "libc", "pkg-config", + "vcpkg", ] [[package]] @@ -1809,7 +1636,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -1943,19 +1770,6 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" -[[package]] -name = "plist" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42cf17e9a1800f5f396bc67d193dc9411b59012a5876445ef450d449881e1016" -dependencies = [ - "base64", - "indexmap", - "quick-xml", - "serde", - "time", -] - [[package]] name = "png" version = "0.17.16" @@ -2013,22 +1827,6 @@ dependencies = [ "zerocopy", ] -[[package]] -name = "precomputed-hash" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" - -[[package]] -name = "prettyplease" -version = "0.2.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5316f57387668042f561aae71480de936257848f9c43ce528e311d89a07cadeb" -dependencies = [ - "proc-macro2", - "syn", -] - [[package]] name = "proc-macro2" version = "1.0.94" @@ -2072,15 +1870,6 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" -[[package]] -name = "quick-xml" -version = "0.32.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d3a6e5838b60e0e8fa7a43f22ade549a37d61f8bdbe636d0d7816191de969c2" -dependencies = [ - "memchr", -] - [[package]] name = "quote" version = "1.0.40" @@ -2096,25 +1885,6 @@ version = "5.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" -[[package]] -name = "rainbeam-shared" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eec83839b59a6485448ad1abf2e1227c6d32a2b6ba872ab0562d86f75de9f4db" -dependencies = [ - "ammonia", - "chrono", - "comrak", - "hex_fmt", - "num-bigint", - "pathbufd", - "rand 0.9.0", - "serde", - "sha2", - "toml", - "uuid", -] - [[package]] name = "rand" version = "0.8.5" @@ -2298,12 +2068,70 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" +[[package]] +name = "reqwest" +version = "0.12.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d19c46a6fdd48bc4dab94b6103fccc55d34c67cc0ad04653aad4ea2a07cd7bbb" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-tls", + "hyper-util", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls-pemfile", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "system-configuration", + "tokio", + "tokio-native-tls", + "tower", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "windows-registry", +] + [[package]] name = "rgb" version = "0.8.50" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57397d16646700483b67d2dd6511d79318f9d057fdbd21a4066aeac8b41d310a" +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.15", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + [[package]] name = "rusqlite" version = "0.34.0" @@ -2337,6 +2165,45 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "rustls" +version = "0.23.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "822ee9188ac4ec04a2f0531e55d035fb2de73f18b41a63c70c2712503b6fb13c" +dependencies = [ + "once_cell", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pemfile" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "917ce264624a4b4db1c364dcc35bfca9ded014d0a958cd47ad3e960e988ea51c" + +[[package]] +name = "rustls-webpki" +version = "0.103.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0aa4eeac2588ffff23e9d7a7e9b3f971c5fb5b7ebc9452745e0c232c64f83b2f" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + [[package]] name = "rustversion" version = "1.0.20" @@ -2358,12 +2225,44 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "schannel" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" +dependencies = [ + "windows-sys 0.59.0", +] + [[package]] name = "scopeguard" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "security-framework" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +dependencies = [ + "bitflags 2.9.0", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "serde" version = "1.0.219" @@ -2447,12 +2346,6 @@ dependencies = [ "lazy_static", ] -[[package]] -name = "shell-words" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" - [[package]] name = "shlex" version = "1.3.0" @@ -2527,31 +2420,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" -[[package]] -name = "string_cache" -version = "0.8.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "938d512196766101d333398efde81bc1f37b00cb42c2f8350e5df639f040bbbe" -dependencies = [ - "new_debug_unreachable", - "parking_lot", - "phf_shared", - "precomputed-hash", - "serde", -] - -[[package]] -name = "string_cache_codegen" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c711928715f1fe0fe509c53b43e993a9a557babc2d0a3567d0a3006f1ac931a0" -dependencies = [ - "phf_generator", - "phf_shared", - "proc-macro2", - "quote", -] - [[package]] name = "stringprep" version = "0.1.5" @@ -2563,12 +2431,6 @@ dependencies = [ "unicode-properties", ] -[[package]] -name = "strsim" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" - [[package]] name = "subtle" version = "2.6.1" @@ -2591,6 +2453,9 @@ name = "sync_wrapper" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] [[package]] name = "synstructure" @@ -2604,26 +2469,24 @@ dependencies = [ ] [[package]] -name = "syntect" -version = "5.2.0" +name = "system-configuration" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "874dcfa363995604333cf947ae9f751ca3af4522c60886774c4963943b4746b1" +checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ - "bincode", - "bitflags 1.3.2", - "fancy-regex", - "flate2", - "fnv", - "once_cell", - "onig", - "plist", - "regex-syntax 0.8.5", - "serde", - "serde_derive", - "serde_json", - "thiserror 1.0.69", - "walkdir", - "yaml-rust", + "bitflags 2.9.0", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" +dependencies = [ + "core-foundation-sys", + "libc", ] [[package]] @@ -2646,14 +2509,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] -name = "tendril" -version = "0.4.3" +name = "tempfile" +version = "3.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d24a120c5fc464a3458240ee02c299ebcb9d67b5249c8848b09d639dca8d7bb0" +checksum = "7437ac7763b9b123ccf33c338a5cc1bac6f69b45a136c19bdd8a65e3916435bf" dependencies = [ - "futf", - "mac", - "utf-8", + "fastrand", + "getrandom 0.3.2", + "once_cell", + "rustix", + "windows-sys 0.59.0", ] [[package]] @@ -2678,16 +2543,6 @@ dependencies = [ "unic-segment", ] -[[package]] -name = "terminal_size" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45c6481c4829e4cc63825e62c49186a34538b7b2750b73b266581ffb612fb5ed" -dependencies = [ - "rustix", - "windows-sys 0.59.0", -] - [[package]] name = "tetratto" version = "0.1.0" @@ -2695,39 +2550,55 @@ dependencies = [ "axum", "axum-extra", "image", - "mime_guess", "pathbufd", - "rainbeam-shared", + "regex", + "reqwest", "serde", - "serde_json", "tera", - "tetratto_core", + "tetratto-core", + "tetratto-l10n", + "tetratto-shared", "tokio", - "toml", "tower-http", "tracing", "tracing-subscriber", ] [[package]] -name = "tetratto_core" +name = "tetratto-core" version = "0.1.0" dependencies = [ - "axum", - "axum-extra", "bb8-postgres", "pathbufd", - "rainbeam-shared", "rusqlite", "serde", "serde_json", - "tera", - "tokio", + "tetratto-l10n", + "tetratto-shared", "tokio-postgres", "toml", - "tower-http", - "tracing", - "tracing-subscriber", +] + +[[package]] +name = "tetratto-l10n" +version = "0.1.0" +dependencies = [ + "pathbufd", + "serde", + "toml", +] + +[[package]] +name = "tetratto-shared" +version = "0.1.0" +dependencies = [ + "chrono", + "hex_fmt", + "num-bigint", + "rand 0.9.0", + "serde", + "sha2", + "uuid", ] [[package]] @@ -2875,6 +2746,16 @@ dependencies = [ "syn", ] +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + [[package]] name = "tokio-postgres" version = "0.7.13" @@ -2901,6 +2782,16 @@ dependencies = [ "whoami", ] +[[package]] +name = "tokio-rustls" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b" +dependencies = [ + "rustls", + "tokio", +] + [[package]] name = "tokio-util" version = "0.7.14" @@ -3064,10 +2955,10 @@ dependencies = [ ] [[package]] -name = "typed-arena" -version = "2.0.2" +name = "try-lock" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "typenum" @@ -3165,10 +3056,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0" [[package]] -name = "unicode_categories" -version = "0.1.1" +name = "untrusted" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" @@ -3181,12 +3072,6 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "utf-8" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" - [[package]] name = "utf16_iter" version = "1.0.5" @@ -3199,12 +3084,6 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" -[[package]] -name = "utf8parse" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" - [[package]] name = "uuid" version = "1.16.0" @@ -3259,6 +3138,15 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -3306,6 +3194,19 @@ dependencies = [ "wasm-bindgen-shared", ] +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" +dependencies = [ + "cfg-if", + "js-sys", + "once_cell", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "wasm-bindgen-macro" version = "0.2.100" @@ -3402,7 +3303,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -3411,13 +3312,42 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" +[[package]] +name = "windows-registry" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4286ad90ddb45071efd1a66dfa43eb02dd0dfbae1545ad6cc3c51cf34d7e8ba3" +dependencies = [ + "windows-result", + "windows-strings", + "windows-targets 0.53.0", +] + +[[package]] +name = "windows-result" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87fa48cc5d406560701792be122a10132491cff9d0aeb23583cc2dcafc847319" +dependencies = [ + "windows-link", +] + [[package]] name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -3426,7 +3356,7 @@ version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -3435,14 +3365,30 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b" +dependencies = [ + "windows_aarch64_gnullvm 0.53.0", + "windows_aarch64_msvc 0.53.0", + "windows_i686_gnu 0.53.0", + "windows_i686_gnullvm 0.53.0", + "windows_i686_msvc 0.53.0", + "windows_x86_64_gnu 0.53.0", + "windows_x86_64_gnullvm 0.53.0", + "windows_x86_64_msvc 0.53.0", ] [[package]] @@ -3451,48 +3397,96 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" + [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" + [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" +[[package]] +name = "windows_i686_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" + [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" + [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_i686_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" + [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" + [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + [[package]] name = "winnow" version = "0.7.4" @@ -3523,21 +3517,6 @@ version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" -[[package]] -name = "xdg" -version = "2.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "213b7324336b53d2414b2db8537e56544d981803139155afa84f76eeebb7a546" - -[[package]] -name = "yaml-rust" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" -dependencies = [ - "linked-hash-map", -] - [[package]] name = "yoke" version = "0.7.5" @@ -3603,6 +3582,12 @@ dependencies = [ "synstructure", ] +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" + [[package]] name = "zerovec" version = "0.10.4" diff --git a/Cargo.toml b/Cargo.toml index d87eb91..ce0c224 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [workspace] -resolver = "3" -members = ["crates/app", "crates/tetratto_core"] +resolver = "2" +members = ["crates/app", "crates/shared", "crates/core", "crates/l10n"] package.authors = ["trisuaso"] package.repository = "https://github.com/trisuaso/tetratto" package.license = "AGPL-3.0-or-later" diff --git a/crates/app/Cargo.toml b/crates/app/Cargo.toml index f11633f..83b9312 100644 --- a/crates/app/Cargo.toml +++ b/crates/app/Cargo.toml @@ -4,23 +4,24 @@ version = "0.1.0" edition = "2024" [features] -postgres = ["tetratto_core/postgres"] -sqlite = ["tetratto_core/sqlite"] +postgres = ["tetratto-core/postgres"] +sqlite = ["tetratto-core/sqlite"] default = ["sqlite"] [dependencies] pathbufd = "0.1.4" serde = { version = "1.0.219", features = ["derive"] } tera = "1.20.0" -toml = "0.8.20" tracing = "0.1.41" tracing-subscriber = { version = "0.3.19", features = ["env-filter"] } tower-http = { version = "0.6.2", features = ["trace", "fs"] } axum = { version = "0.8.1", features = ["macros"] } -tokio = { version = "1.44.0", features = ["macros", "rt-multi-thread"] } -rainbeam-shared = "1.0.1" -serde_json = "1.0.140" +tokio = { version = "1.44.1", features = ["macros", "rt-multi-thread"] } axum-extra = { version = "0.10.0", features = ["cookie", "multipart"] } -tetratto_core = { path = "../tetratto_core", default-features = false } +tetratto-shared = { path = "../shared" } +tetratto-core = { path = "../core", default-features = false } +tetratto-l10n = { path = "../l10n" } + image = "0.25.5" -mime_guess = "2.0.5" +reqwest = "0.12.15" +regex = "1.11.1" diff --git a/crates/app/src/assets.rs b/crates/app/src/assets.rs index dabdfb1..76603d3 100644 --- a/crates/app/src/assets.rs +++ b/crates/app/src/assets.rs @@ -1,12 +1,21 @@ use pathbufd::PathBufD; +use regex::Regex; +use std::{ + collections::HashMap, + fs::{exists, read_to_string, write}, + sync::LazyLock, +}; use tera::Context; use tetratto_core::{config::Config, model::auth::User}; +use tetratto_l10n::LangFile; +use tokio::sync::RwLock; -use crate::write_template; +use crate::{create_dir_if_not_exists, write_if_track, write_template}; // images pub const DEFAULT_AVATAR: &str = include_str!("./public/images/default-avatar.svg"); pub const DEFAULT_BANNER: &str = include_str!("./public/images/default-banner.svg"); +pub const FAVICON: &str = include_str!("./public/images/favicon.svg"); // css pub const STYLE_CSS: &str = include_str!("./public/css/style.css"); @@ -25,24 +34,142 @@ pub const AUTH_BASE: &str = include_str!("./public/html/auth/base.html"); pub const AUTH_LOGIN: &str = include_str!("./public/html/auth/login.html"); pub const AUTH_REGISTER: &str = include_str!("./public/html/auth/register.html"); +// langs +pub const LANG_EN_US: &str = include_str!("./langs/en-US.toml"); + // ... +/// A container for all loaded icons. +pub(crate) static ICONS: LazyLock>> = + LazyLock::new(|| RwLock::new(HashMap::new())); + +/// Pull an icon given its name and insert it into [`ICONS`]. +pub(crate) async fn pull_icon(icon: &str, icons_dir: &str) { + let writer = &mut ICONS.write().await; + + let icon_url = format!( + "https://raw.githubusercontent.com/lucide-icons/lucide/refs/heads/main/icons/{icon}.svg" + ); + + let file_path = PathBufD::current().extend(&[icons_dir, icon]); + + if exists(&file_path).unwrap() { + writer.insert(icon.to_string(), read_to_string(&file_path).unwrap()); + return; + } + + println!("download icon: {icon}"); + let svg = reqwest::get(icon_url).await.unwrap().text().await.unwrap(); + + write(&file_path, &svg).unwrap(); + writer.insert(icon.to_string(), svg); +} + +/// Read a string and replace all custom blocks with the corresponding correct HTML. +/// +/// # Replaces +/// * icons +/// * icons (with class specifier) +/// * l10n text +pub(crate) async fn replace_in_html(input: &str, config: &Config) -> String { + let mut input = input.to_string(); + input = input.replace("", ""); + + // l10n text + let text = Regex::new("(\\{\\{)\\s*(text)\\s*\"(.*?)\"\\s*(\\}\\})").unwrap(); + + for cap in text.captures_iter(&input.clone()) { + let replace_with = format!("{{{{ lang[\"{}\"] }}}}", cap.get(3).unwrap().as_str()); + input = input.replace(cap.get(0).unwrap().as_str(), &replace_with); + } + + // icon (with class) + let icon_with_class = + Regex::new("(\\{\\{)\\s*(icon)\\s*(.*?)\\s*c\\((.*?)\\)\\s*(\\}\\})").unwrap(); + + for cap in icon_with_class.captures_iter(&input.clone()) { + let icon = &cap.get(3).unwrap().as_str().replace("\"", ""); + + pull_icon(icon, &config.dirs.icons).await; + + let reader = ICONS.read().await; + let icon_text = reader.get(icon).unwrap().replace( + ""root.html"(crate::assets::ROOT)); - write_template!(html_path->"macros.html"(crate::assets::MACROS)); +pub(crate) async fn write_assets(config: &Config) -> PathBufD { + let html_path = PathBufD::current().join(&config.dirs.templates); - write_template!(html_path->"misc/index.html"(crate::assets::MISC_INDEX) -d "misc"); + write_template!(html_path->"root.html"(crate::assets::ROOT) --config=config); + write_template!(html_path->"macros.html"(crate::assets::MACROS) --config=config); - write_template!(html_path->"auth/base.html"(crate::assets::AUTH_BASE) -d "auth"); - write_template!(html_path->"auth/login.html"(crate::assets::AUTH_LOGIN)); - write_template!(html_path->"auth/register.html"(crate::assets::AUTH_REGISTER)); + write_template!(html_path->"misc/index.html"(crate::assets::MISC_INDEX) -d "misc" --config=config); + + write_template!(html_path->"auth/base.html"(crate::assets::AUTH_BASE) -d "auth" --config=config); + write_template!(html_path->"auth/login.html"(crate::assets::AUTH_LOGIN) --config=config); + write_template!(html_path->"auth/register.html"(crate::assets::AUTH_REGISTER) --config=config); + + html_path +} + +/// Set up extra directories. +pub(crate) async fn init_dirs(config: &Config) { + // images + create_dir_if_not_exists!(&config.dirs.media); + let images_path = PathBufD::current().extend(&[config.dirs.media.as_str(), "images"]); + create_dir_if_not_exists!(&images_path); + create_dir_if_not_exists!( + &PathBufD::current().extend(&[config.dirs.media.as_str(), "avatars"]) + ); + create_dir_if_not_exists!( + &PathBufD::current().extend(&[config.dirs.media.as_str(), "banners"]) + ); + + write_if_track!(images_path->"default-avatar.svg"(DEFAULT_AVATAR) --config=config); + write_if_track!(images_path->"default-banner.svg"(DEFAULT_BANNER) --config=config); + write_if_track!(images_path->"favicon.svg"(FAVICON) --config=config); + + // icons + create_dir_if_not_exists!(&PathBufD::current().join(config.dirs.icons.as_str())); + + // langs + let langs_path = PathBufD::current().join("langs"); + create_dir_if_not_exists!(&langs_path); + + write_template!(langs_path->"en-US.toml"(LANG_EN_US)); } /// Create the initial template context. -pub(crate) fn initial_context(config: &Config, user: &Option) -> Context { +pub(crate) fn initial_context(config: &Config, lang: &LangFile, user: &Option) -> Context { let mut ctx = Context::new(); ctx.insert("config", &config); ctx.insert("user", &user); + ctx.insert("lang", &lang); ctx } diff --git a/crates/app/src/langs/en-US.toml b/crates/app/src/langs/en-US.toml new file mode 100644 index 0000000..b9b4f00 --- /dev/null +++ b/crates/app/src/langs/en-US.toml @@ -0,0 +1,6 @@ +name = "com.tetratto.langs:en-US" +version = "1.0.0" + +[data] +"general:action.login" = "Login" +"general:action.register" = "Register" diff --git a/crates/app/src/macros.rs b/crates/app/src/macros.rs index 64bb21b..5d9f90d 100644 --- a/crates/app/src/macros.rs +++ b/crates/app/src/macros.rs @@ -1,16 +1,46 @@ #[macro_export] macro_rules! write_template { - ($html_path:ident->$path:literal($as:expr)) => { - std::fs::write($html_path.join($path), $as).unwrap(); + ($into:ident->$path:literal($as:expr)) => { + std::fs::write($into.join($path), $as).unwrap(); }; - ($html_path:ident->$path:literal($as:expr) -d $dir_path:literal) => { - let dir = $html_path.join($dir_path); + ($into:ident->$path:literal($as:expr) --config=$config:ident) => { + std::fs::write( + $into.join($path), + crate::assets::replace_in_html($as, &$config).await, + ) + .unwrap(); + }; + + ($into:ident->$path:literal($as:expr) -d $dir_path:literal) => { + let dir = $into.join($dir_path); if !std::fs::exists(&dir).unwrap() { std::fs::create_dir(dir).unwrap(); } - std::fs::write($html_path.join($path), $as).unwrap(); + std::fs::write($into.join($path), $as).unwrap(); + }; + + ($into:ident->$path:literal($as:expr) -d $dir_path:literal --config=$config:ident) => { + let dir = $into.join($dir_path); + if !std::fs::exists(&dir).unwrap() { + std::fs::create_dir(dir).unwrap(); + } + + std::fs::write( + $into.join($path), + crate::assets::replace_in_html($as, &$config).await, + ) + .unwrap(); + }; +} + +#[macro_export] +macro_rules! write_if_track { + ($into:ident->$path:literal($as:expr) --config=$config:ident) => { + if !$config.no_track.contains(&$path.to_string()) { + write_template!($into->$path($as)); + } }; } @@ -28,7 +58,7 @@ macro_rules! get_user_from_token { (($jar:ident, $db:expr) ) => {{ if let Some(token) = $jar.get("__Secure-atto-token") { match $db - .get_user_by_token(&rainbeam_shared::hash::hash( + .get_user_by_token(&tetratto_shared::hash::hash( token.to_string().replace("__Secure-atto-token=", ""), )) .await @@ -52,3 +82,20 @@ macro_rules! get_user_from_token { } }}; } + +#[macro_export] +macro_rules! get_lang { + ($jar:ident, $db:expr) => {{ + if let Some(lang) = $jar.get("__Secure-atto-lang") { + match $db + .1 + .get(&lang.to_string().replace("__Secure-atto-lang=", "")) + { + Some(lang) => lang, + None => $db.1.get("com.tetratto.langs:en-US").unwrap(), + } + } else { + $db.1.get("com.tetratto.langs:en-US").unwrap() + } + }}; +} diff --git a/crates/app/src/main.rs b/crates/app/src/main.rs index ad74e5c..ae0e79d 100644 --- a/crates/app/src/main.rs +++ b/crates/app/src/main.rs @@ -3,11 +3,10 @@ mod avif; mod macros; mod routes; -use assets::write_assets; +use assets::{init_dirs, write_assets}; pub use tetratto_core::*; use axum::{Extension, Router}; -use pathbufd::PathBufD; use tera::Tera; use tower_http::trace::{self, TraceLayer}; use tracing::{Level, info}; @@ -26,24 +25,9 @@ async fn main() { let config = config::Config::get_config(); - // ... - create_dir_if_not_exists!(&config.dirs.media); - let images_path = - PathBufD::current().extend(&[config.dirs.media.clone(), "images".to_string()]); - create_dir_if_not_exists!(&images_path); - create_dir_if_not_exists!( - &PathBufD::current().extend(&[config.dirs.media.clone(), "avatars".to_string()]) - ); - create_dir_if_not_exists!( - &PathBufD::current().extend(&[config.dirs.media.clone(), "banners".to_string()]) - ); - - write_template!(images_path->"default-avatar.svg"(assets::DEFAULT_AVATAR)); - write_template!(images_path->"default-banner.svg"(assets::DEFAULT_BANNER)); - - // create templates - let html_path = PathBufD::current().join(&config.dirs.templates); - write_assets(&html_path); + // init + init_dirs(&config).await; + let html_path = write_assets(&config).await; // ... let app = Router::new() diff --git a/crates/app/src/public/css/style.css b/crates/app/src/public/css/style.css index ce7b3d3..f69a77b 100644 --- a/crates/app/src/public/css/style.css +++ b/crates/app/src/public/css/style.css @@ -132,9 +132,17 @@ footer { } /* typo */ -.icon { - color: inherit; +svg.icon { stroke: currentColor; + width: 18px; +} + +svg.icon.filled { + fill: currentColor; +} + +button svg { + pointer-events: none; } hr { @@ -666,9 +674,7 @@ dialog[open] { .dropdown .inner button { width: 100%; padding: 0.25rem var(--horizontal-padding); - /* transition: - background 0.1s, - transform 0.15s; */ + transition: none !important; text-decoration: none; display: flex; align-items: center; diff --git a/crates/app/src/public/html/auth/login.html b/crates/app/src/public/html/auth/login.html index f35c90a..6115710 100644 --- a/crates/app/src/public/html/auth/login.html +++ b/crates/app/src/public/html/auth/login.html @@ -1,5 +1,5 @@ {% extends "auth/base.html" %} {% block head %} -🐐 Login +Login {% endblock %} {% block title %}Login{% endblock %} {% block content %}
diff --git a/crates/app/src/public/html/auth/register.html b/crates/app/src/public/html/auth/register.html index e6c1607..8349d18 100644 --- a/crates/app/src/public/html/auth/register.html +++ b/crates/app/src/public/html/auth/register.html @@ -1,5 +1,5 @@ {% extends "auth/base.html" %} {% block head %} -🐐 Register +Register {% endblock %} {% block title %}Register{% endblock %} {% block content %}
diff --git a/crates/app/src/public/html/macros.html b/crates/app/src/public/html/macros.html index 834fd66..3acf16b 100644 --- a/crates/app/src/public/html/macros.html +++ b/crates/app/src/public/html/macros.html @@ -10,14 +10,17 @@ Home + {{ icon "house" }} + Home + {% endif %}
diff --git a/crates/app/src/public/html/root.html b/crates/app/src/public/html/root.html index 4c675cd..7481cea 100644 --- a/crates/app/src/public/html/root.html +++ b/crates/app/src/public/html/root.html @@ -5,6 +5,13 @@ + + + + diff --git a/crates/app/src/public/images/default-avatar.svg b/crates/app/src/public/images/default-avatar.svg index c53bc6f..a4cf241 100644 --- a/crates/app/src/public/images/default-avatar.svg +++ b/crates/app/src/public/images/default-avatar.svg @@ -1,3 +1,9 @@ - - + + diff --git a/crates/app/src/public/images/default-banner.svg b/crates/app/src/public/images/default-banner.svg index c53bc6f..a8edad2 100644 --- a/crates/app/src/public/images/default-banner.svg +++ b/crates/app/src/public/images/default-banner.svg @@ -1,3 +1,9 @@ - - + + diff --git a/crates/app/src/public/images/favicon.svg b/crates/app/src/public/images/favicon.svg new file mode 100644 index 0000000..c3dd4d9 --- /dev/null +++ b/crates/app/src/public/images/favicon.svg @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + diff --git a/crates/app/src/routes/assets.rs b/crates/app/src/routes/assets.rs index a56bfe9..5e7144c 100644 --- a/crates/app/src/routes/assets.rs +++ b/crates/app/src/routes/assets.rs @@ -1,5 +1,10 @@ use axum::response::IntoResponse; +/// `/public/favicon.svg` +pub async fn favicon_request() -> impl IntoResponse { + ([("Content-Type", "image/svg+xml")], crate::assets::FAVICON) +} + /// `/css/style.css` pub async fn style_css_request() -> impl IntoResponse { ([("Content-Type", "text/css")], crate::assets::STYLE_CSS) diff --git a/crates/app/src/routes/mod.rs b/crates/app/src/routes/mod.rs index d6b24a9..4ec6673 100644 --- a/crates/app/src/routes/mod.rs +++ b/crates/app/src/routes/mod.rs @@ -15,9 +15,10 @@ pub fn routes(config: &Config) -> Router { .route("/js/atto.js", get(assets::atto_js_request)) .route("/js/loader.js", get(assets::loader_js_request)) .nest_service( - "/static", + "/public", get_service(tower_http::services::ServeDir::new(&config.dirs.assets)), ) + .route("/public/favicon.svg", get(assets::favicon_request)) // api .nest("/api/v1", api::v1::routes()) // pages diff --git a/crates/app/src/routes/pages/auth.rs b/crates/app/src/routes/pages/auth.rs index 9831c3c..d162722 100644 --- a/crates/app/src/routes/pages/auth.rs +++ b/crates/app/src/routes/pages/auth.rs @@ -1,4 +1,4 @@ -use crate::{State, assets::initial_context, get_user_from_token}; +use crate::{State, assets::initial_context, get_lang, get_user_from_token}; use axum::{ Extension, response::{Html, IntoResponse, Redirect}, @@ -14,7 +14,9 @@ pub async fn login_request(jar: CookieJar, Extension(data): Extension) -> return Err(Redirect::to("/")); } - let mut context = initial_context(&data.0.0, &user); + let lang = get_lang!(jar, data.0); + let mut context = initial_context(&data.0.0, lang, &user); + Ok(Html( data.1.render("auth/login.html", &mut context).unwrap(), )) @@ -32,7 +34,9 @@ pub async fn register_request( return Err(Redirect::to("/")); } - let mut context = initial_context(&data.0.0, &user); + let lang = get_lang!(jar, data.0); + let mut context = initial_context(&data.0.0, lang, &user); + Ok(Html( data.1.render("auth/register.html", &mut context).unwrap(), )) diff --git a/crates/app/src/routes/pages/misc.rs b/crates/app/src/routes/pages/misc.rs index eab6bcd..53c71f9 100644 --- a/crates/app/src/routes/pages/misc.rs +++ b/crates/app/src/routes/pages/misc.rs @@ -1,4 +1,4 @@ -use crate::{State, assets::initial_context, get_user_from_token}; +use crate::{State, assets::initial_context, get_lang, get_user_from_token}; use axum::{ Extension, response::{Html, IntoResponse}, @@ -10,6 +10,8 @@ pub async fn index_request(jar: CookieJar, Extension(data): Extension) -> let data = data.read().await; let user = get_user_from_token!((jar, data.0) ); - let mut context = initial_context(&data.0.0, &user); + let lang = get_lang!(jar, data.0); + let mut context = initial_context(&data.0.0, lang, &user); + Html(data.1.render("misc/index.html", &mut context).unwrap()) } diff --git a/crates/tetratto_core/Cargo.toml b/crates/core/Cargo.toml similarity index 52% rename from crates/tetratto_core/Cargo.toml rename to crates/core/Cargo.toml index 3062a99..22f09b9 100644 --- a/crates/tetratto_core/Cargo.toml +++ b/crates/core/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "tetratto_core" +name = "tetratto-core" version = "0.1.0" edition = "2024" @@ -11,16 +11,10 @@ default = ["sqlite"] [dependencies] pathbufd = "0.1.4" serde = { version = "1.0.219", features = ["derive"] } -tera = "1.20.0" toml = "0.8.20" -tracing = "0.1.41" -tracing-subscriber = { version = "0.3.19", features = ["env-filter"] } -tower-http = { version = "0.6.2", features = ["trace", "fs"] } -axum = { version = "0.8.1", features = ["macros"] } -tokio = { version = "1.44.0", features = ["macros", "rt-multi-thread"] } -rainbeam-shared = "1.0.1" +tetratto-shared = { path = "../shared" } +tetratto-l10n = { path = "../l10n" } serde_json = "1.0.140" -axum-extra = { version = "0.10.0", features = ["cookie"] } rusqlite = { version = "0.34.0", optional = true } diff --git a/crates/tetratto_core/LICENSE b/crates/core/LICENSE similarity index 100% rename from crates/tetratto_core/LICENSE rename to crates/core/LICENSE diff --git a/crates/tetratto_core/src/config.rs b/crates/core/src/config.rs similarity index 90% rename from crates/tetratto_core/src/config.rs rename to crates/core/src/config.rs index 26d2201..e40a72c 100644 --- a/crates/tetratto_core/src/config.rs +++ b/crates/core/src/config.rs @@ -51,6 +51,9 @@ pub struct DirsConfig { /// Media (user avatars/banners) files directory. #[serde(default = "default_dir_media")] pub media: String, + /// The icons files directory. + #[serde(default = "default_dir_icons")] + pub icons: String, } fn default_dir_templates() -> String { @@ -65,12 +68,17 @@ fn default_dir_media() -> String { "media".to_string() } +fn default_dir_icons() -> String { + "icons".to_string() +} + impl Default for DirsConfig { fn default() -> Self { Self { templates: default_dir_templates(), assets: default_dir_assets(), media: default_dir_media(), + icons: default_dir_icons(), } } } @@ -122,8 +130,13 @@ pub struct Config { /// The locations where different files should be matched. #[serde(default = "default_dirs")] pub dirs: DirsConfig, + /// Database configuration. #[serde(default = "default_database")] pub database: DatabaseConfig, + /// A list of files (just their name, no full path) which are NOT updated to match the + /// version built with the server binary. + #[serde(default = "default_no_track")] + pub no_track: Vec, } fn default_name() -> String { @@ -153,6 +166,10 @@ fn default_database() -> DatabaseConfig { DatabaseConfig::default() } +fn default_no_track() -> Vec { + Vec::new() +} + impl Default for Config { fn default() -> Self { Self { @@ -163,6 +180,7 @@ impl Default for Config { database: default_database(), security: default_security(), dirs: default_dirs(), + no_track: default_no_track(), } } } diff --git a/crates/tetratto_core/src/database/auth.rs b/crates/core/src/database/auth.rs similarity index 99% rename from crates/tetratto_core/src/database/auth.rs rename to crates/core/src/database/auth.rs index a21af56..857edf9 100644 --- a/crates/tetratto_core/src/database/auth.rs +++ b/crates/core/src/database/auth.rs @@ -2,7 +2,7 @@ use super::*; use crate::model::{Error, Result}; use crate::{execute, get, query_row}; -use rainbeam_shared::hash::hash_salted; +use tetratto_shared::hash::hash_salted; #[cfg(feature = "sqlite")] use rusqlite::Row; diff --git a/crates/tetratto_core/src/database/drivers/common.rs b/crates/core/src/database/drivers/common.rs similarity index 100% rename from crates/tetratto_core/src/database/drivers/common.rs rename to crates/core/src/database/drivers/common.rs diff --git a/crates/tetratto_core/src/database/drivers/mod.rs b/crates/core/src/database/drivers/mod.rs similarity index 100% rename from crates/tetratto_core/src/database/drivers/mod.rs rename to crates/core/src/database/drivers/mod.rs diff --git a/crates/tetratto_core/src/database/drivers/postgres.rs b/crates/core/src/database/drivers/postgres.rs similarity index 89% rename from crates/tetratto_core/src/database/drivers/postgres.rs rename to crates/core/src/database/drivers/postgres.rs index 0123360..5f25907 100644 --- a/crates/tetratto_core/src/database/drivers/postgres.rs +++ b/crates/core/src/database/drivers/postgres.rs @@ -3,18 +3,23 @@ use bb8_postgres::{ PostgresConnectionManager, bb8::{Pool, PooledConnection}, }; +use tetratto_l10n::{LangFile, read_langs}; use tokio_postgres::{Config as PgConfig, NoTls, Row, types::ToSql}; pub type Result = std::result::Result; pub type Connection<'a> = PooledConnection<'a, PostgresConnectionManager>; #[derive(Clone)] -pub struct DataManager(pub Config, pub Pool>); +pub struct DataManager( + pub Config, + pub HashMap, + pub Pool>, +); impl DataManager { /// Obtain a connection to the staging database. pub(crate) async fn connect(&self) -> Result { - Ok(self.1.get().await.unwrap()) + Ok(self.2.get().await.unwrap()) } /// Create a new [`DataManager`] (and init database). @@ -31,7 +36,7 @@ impl DataManager { ); let pool = Pool::builder().max_size(15).build(manager).await.unwrap(); - let this = Self(config.clone(), pool); + let this = Self(config.clone(), read_langs(), pool); let c = this.clone(); let conn = c.connect().await?; diff --git a/crates/tetratto_core/src/database/drivers/sql/create_users.sql b/crates/core/src/database/drivers/sql/create_users.sql similarity index 100% rename from crates/tetratto_core/src/database/drivers/sql/create_users.sql rename to crates/core/src/database/drivers/sql/create_users.sql diff --git a/crates/tetratto_core/src/database/drivers/sqlite.rs b/crates/core/src/database/drivers/sqlite.rs similarity index 84% rename from crates/tetratto_core/src/database/drivers/sqlite.rs rename to crates/core/src/database/drivers/sqlite.rs index 70c4385..052b623 100644 --- a/crates/tetratto_core/src/database/drivers/sqlite.rs +++ b/crates/core/src/database/drivers/sqlite.rs @@ -1,8 +1,10 @@ use crate::config::Config; use rusqlite::{Connection, Result}; +use std::collections::HashMap; +use tetratto_l10n::{LangFile, read_langs}; #[derive(Clone)] -pub struct DataManager(pub Config); +pub struct DataManager(pub Config, pub HashMap); impl DataManager { /// Obtain a connection to the staging database. @@ -12,7 +14,7 @@ impl DataManager { /// Create a new [`DataManager`] (and init database). pub async fn new(config: Config) -> Result { - let this = Self(config.clone()); + let this = Self(config.clone(), read_langs()); let conn = this.connect().await?; conn.pragma_update(None, "journal_mode", "WAL").unwrap(); diff --git a/crates/tetratto_core/src/database/mod.rs b/crates/core/src/database/mod.rs similarity index 100% rename from crates/tetratto_core/src/database/mod.rs rename to crates/core/src/database/mod.rs diff --git a/crates/tetratto_core/src/lib.rs b/crates/core/src/lib.rs similarity index 100% rename from crates/tetratto_core/src/lib.rs rename to crates/core/src/lib.rs diff --git a/crates/tetratto_core/src/model/auth.rs b/crates/core/src/model/auth.rs similarity index 92% rename from crates/tetratto_core/src/model/auth.rs rename to crates/core/src/model/auth.rs index 9fcef2d..6faaafc 100644 --- a/crates/tetratto_core/src/model/auth.rs +++ b/crates/core/src/model/auth.rs @@ -1,9 +1,9 @@ -use rainbeam_shared::{ +use serde::{Deserialize, Serialize}; +use tetratto_shared::{ hash::{hash_salted, salt}, snow::AlmostSnowflake, unix_epoch_timestamp, }; -use serde::{Deserialize, Serialize}; /// `(ip, token, creation timestamp)` pub type Token = (String, String, usize); @@ -53,12 +53,12 @@ impl User { /// # Returns /// `(unhashed id, token)` pub fn create_token(ip: &str) -> (String, Token) { - let unhashed = rainbeam_shared::hash::uuid(); + let unhashed = tetratto_shared::hash::uuid(); ( unhashed.clone(), ( ip.to_string(), - rainbeam_shared::hash::hash(unhashed), + tetratto_shared::hash::hash(unhashed), unix_epoch_timestamp() as usize, ), ) diff --git a/crates/tetratto_core/src/model/mod.rs b/crates/core/src/model/mod.rs similarity index 100% rename from crates/tetratto_core/src/model/mod.rs rename to crates/core/src/model/mod.rs diff --git a/crates/l10n/Cargo.toml b/crates/l10n/Cargo.toml new file mode 100644 index 0000000..1b0e473 --- /dev/null +++ b/crates/l10n/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "tetratto-l10n" +version = "0.1.0" +edition = "2024" +authors.workspace = true +repository.workspace = true +license.workspace = true + +[dependencies] +pathbufd = "0.1.4" +serde = { version = "1.0.219", features = ["derive"] } +toml = "0.8.20" diff --git a/crates/l10n/src/lib.rs b/crates/l10n/src/lib.rs new file mode 100644 index 0000000..3840c53 --- /dev/null +++ b/crates/l10n/src/lib.rs @@ -0,0 +1,95 @@ +use pathbufd::PathBufD; +use serde::{Deserialize, Serialize}; +use std::{ + collections::HashMap, + fs::{read_dir, read_to_string}, + sync::{LazyLock, RwLock}, +}; + +pub static ENGLISH_US: LazyLock> = LazyLock::new(RwLock::default); + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct LangFile { + pub name: String, + pub version: String, + pub data: HashMap, +} + +impl Default for LangFile { + fn default() -> Self { + Self { + name: "com.tettrato.langs.testing:aa-BB".to_string(), + version: "0.0.0".to_string(), + data: HashMap::new(), + } + } +} + +impl LangFile { + /// Check if a value exists in `data` (and isn't empty) + pub fn exists(&self, key: &str) -> bool { + if let Some(value) = self.data.get(key) { + if value.is_empty() { + return false; + } + + return true; + } + + false + } + + /// Get a value from `data`, returns an empty string if it doesn't exist + pub fn get(&self, key: &str) -> String { + if !self.exists(key) { + if (self.name == "com.tettrato.langs.testing:aa-BB") + | (self.name == "com.tettrato.langs.testing:en-US") + { + return key.to_string(); + } else { + // load english instead + let reader = ENGLISH_US + .read() + .expect("failed to pull reader for ENGLISH_US"); + return reader.get(key); + } + } + + self.data.get(key).unwrap().to_owned() + } +} + +/// Read the `langs` directory and return a [`Hashmap`] containing all files +pub fn read_langs() -> HashMap { + let mut out = HashMap::new(); + + let langs_dir = PathBufD::current().join("langs"); + if let Ok(files) = read_dir(langs_dir) { + for file in files.into_iter() { + if file.is_err() { + continue; + } + + let de: LangFile = match toml::from_str(&match read_to_string(file.unwrap().path()) { + Ok(f) => f, + Err(_) => continue, + }) { + Ok(de) => de, + Err(_) => continue, + }; + + if de.name.ends_with("en-US") { + let mut writer = ENGLISH_US + .write() + .expect("failed to pull writer for ENGLISH_US"); + *writer = de.clone(); + drop(writer); + } + + out.insert(de.name.clone(), de); + } + } + + // return + out +} diff --git a/crates/shared/Cargo.toml b/crates/shared/Cargo.toml new file mode 100644 index 0000000..a1fbac8 --- /dev/null +++ b/crates/shared/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "tetratto-shared" +version = "0.1.0" +edition = "2024" +authors.workspace = true +repository.workspace = true +license.workspace = true + +[dependencies] +chrono = "0.4.40" +hex_fmt = "0.3.0" +num-bigint = "0.4.6" +rand = "0.9.0" +serde = "1.0.219" +sha2 = "0.10.8" +uuid = { version = "1.16.0", features = ["v4"] } diff --git a/crates/shared/src/hash.rs b/crates/shared/src/hash.rs new file mode 100644 index 0000000..f346861 --- /dev/null +++ b/crates/shared/src/hash.rs @@ -0,0 +1,38 @@ +use hex_fmt::HexFmt; +use rand::{Rng, distr::Alphanumeric, rng}; +use sha2::{Digest, Sha256}; +use uuid::Uuid; + +// ids +pub fn uuid() -> String { + let uuid = Uuid::new_v4(); + uuid.to_string() +} + +pub fn hash(input: String) -> String { + let mut hasher = ::new(); + hasher.update(input.into_bytes()); + + let res = hasher.finalize(); + HexFmt(res).to_string() +} + +pub fn hash_salted(input: String, salt: String) -> String { + let mut hasher = ::new(); + hasher.update(format!("{salt}{input}").into_bytes()); + + let res = hasher.finalize(); + HexFmt(res).to_string() +} + +pub fn salt() -> String { + rng() + .sample_iter(&Alphanumeric) + .take(16) + .map(char::from) + .collect() +} + +pub fn random_id() -> String { + hash(uuid()) +} diff --git a/crates/shared/src/lib.rs b/crates/shared/src/lib.rs new file mode 100644 index 0000000..5a7f9c3 --- /dev/null +++ b/crates/shared/src/lib.rs @@ -0,0 +1,5 @@ +pub mod hash; +pub mod snow; +pub mod time; + +pub use time::{epoch_timestamp, unix_epoch_timestamp}; diff --git a/crates/shared/src/snow.rs b/crates/shared/src/snow.rs new file mode 100644 index 0000000..24f21d8 --- /dev/null +++ b/crates/shared/src/snow.rs @@ -0,0 +1,52 @@ +//! Almost Snowflake +//! +//! Random IDs which include timestamp information (like Twitter Snowflakes) +//! +//! IDs are generated with 41 bits of an epoch timestamp, 10 bits of a machine/server ID, and 12 bits of randomly generated numbers. +//! +//! ``` +//! tttttttttttttttttttttttttttttttttttttttttiiiiiiiiiirrrrrrrrrrrr... +//! Timestamp ID Seed +//! ``` +use crate::epoch_timestamp; +use serde::{Deserialize, Serialize}; + +use num_bigint::BigInt; +use rand::Rng; + +static SEED_LEN: usize = 12; +// static ID_LEN: usize = 10; + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub struct AlmostSnowflake(String); + +pub fn bigint(input: usize) -> BigInt { + BigInt::from(input) +} + +impl AlmostSnowflake { + /// Create a new [`AlmostSnowflake`] + pub fn new(server_id: usize) -> Self { + // generate random bytes + let mut bytes = String::new(); + + let mut rng = rand::rng(); + for _ in 1..=SEED_LEN { + bytes.push_str(&rng.random_range(0..10).to_string()) + } + + // build id + let mut id = bigint(epoch_timestamp(2024) as usize) << 22_u128; + id |= bigint((server_id % 1024) << 12); + id |= bigint((bytes.parse::().unwrap() + 1) % 4096); + + // return + Self(id.to_string()) + } +} + +impl std::fmt::Display for AlmostSnowflake { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0) + } +} diff --git a/crates/shared/src/time.rs b/crates/shared/src/time.rs new file mode 100644 index 0000000..a2ad900 --- /dev/null +++ b/crates/shared/src/time.rs @@ -0,0 +1,23 @@ +use chrono::{TimeZone, Utc}; +use std::time::{SystemTime, UNIX_EPOCH}; + +/// Get a [`u128`] timestamp +pub fn unix_epoch_timestamp() -> u128 { + let right_now = SystemTime::now(); + let time_since = right_now + .duration_since(UNIX_EPOCH) + .expect("Time travel is not allowed"); + + time_since.as_millis() +} + +/// Get a [`i64`] timestamp from the given `year` epoch +pub fn epoch_timestamp(year: i32) -> i64 { + let now = Utc::now().timestamp_millis(); + let then = Utc + .with_ymd_and_hms(year, 1, 1, 0, 0, 0) + .unwrap() + .timestamp_millis(); + + now - then +} diff --git a/example/.gitignore b/example/.gitignore index db9b0fd..6ecb124 100644 --- a/example/.gitignore +++ b/example/.gitignore @@ -7,3 +7,5 @@ public/* !public/.gitkeep media/* +icons/* +langs/*