diff options
| author | mrw1593 <botahamec@outlook.com> | 2023-05-15 21:42:47 -0400 |
|---|---|---|
| committer | mrw1593 <botahamec@outlook.com> | 2023-05-29 10:46:04 -0400 |
| commit | e38854c7db0fe6f006304d7f638b6aa190fc2d87 (patch) | |
| tree | eb730768a5f9c68f20c3b20a180cb1ab327a2de5 | |
| parent | 3f0cfb69f2ab1dda4425fe871a6cbf3b4bfb8dc3 (diff) | |
Started on frontend
| -rw-r--r-- | Cargo.lock | 669 | ||||
| -rw-r--r-- | Cargo.toml | 7 | ||||
| -rw-r--r-- | src/api/liveops.rs | 4 | ||||
| -rw-r--r-- | src/api/ops.rs | 20 | ||||
| -rw-r--r-- | src/api/users.rs | 24 | ||||
| -rw-r--r-- | src/main.rs | 19 | ||||
| -rw-r--r-- | src/resources/languages.rs | 67 | ||||
| -rw-r--r-- | src/resources/mod.rs | 4 | ||||
| -rw-r--r-- | src/resources/scripts.rs | 37 | ||||
| -rw-r--r-- | src/resources/style.rs | 53 | ||||
| -rw-r--r-- | src/resources/templates.rs | 59 | ||||
| -rw-r--r-- | static/languages/en.ini | 6 | ||||
| -rw-r--r-- | static/scripts/main.js | 0 | ||||
| -rw-r--r-- | static/style/style.scss | 0 | ||||
| -rw-r--r-- | static/templates/base.html | 16 | ||||
| -rw-r--r-- | static/templates/login.html | 11 |
16 files changed, 968 insertions, 28 deletions
@@ -237,6 +237,24 @@ dependencies = [ ] [[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 = "ansi_term" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi", +] + +[[package]] name = "arrayref" version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -258,6 +276,17 @@ dependencies = [ ] [[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi 0.1.19", + "libc", + "winapi", +] + +[[package]] name = "autocfg" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -329,6 +358,16 @@ dependencies = [ ] [[package]] +name = "bstr" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d4260bcc2e8fc9df1eac4919a720effeb63a3f0952f5bf4944adfa18897f09" +dependencies = [ + "memchr", + "serde", +] + +[[package]] name = "bumpalo" version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -371,6 +410,61 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] +name = "chrono" +version = "0.4.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e3c5919066adf22df73762e50cffcde3a758f2a848b113b586d1f86728b673b" +dependencies = [ + "iana-time-zone", + "num-integer", + "num-traits", + "winapi", +] + +[[package]] +name = "chrono-tz" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58549f1842da3080ce63002102d5bc954c7bc843d4f47818e642abdc36253552" +dependencies = [ + "chrono", + "chrono-tz-build", + "phf", +] + +[[package]] +name = "chrono-tz-build" +version = "0.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db058d493fb2f65f41861bfed7e3fe6335264a9f0f92710cab5bdf01fef09069" +dependencies = [ + "parse-zoneinfo", + "phf", + "phf_codegen", +] + +[[package]] +name = "clap" +version = "2.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" +dependencies = [ + "ansi_term", + "atty", + "bitflags", + "strsim", + "textwrap", + "unicode-width", + "vec_map", +] + +[[package]] +name = "codemap" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e769b5c8c8283982a987c6e948e540254f1058d5a74b8794914d4ef5fc2a24" + +[[package]] name = "const-oid" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -406,6 +500,12 @@ dependencies = [ ] [[package]] +name = "core-foundation-sys" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" + +[[package]] name = "cpufeatures" version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -502,6 +602,12 @@ dependencies = [ ] [[package]] +name = "deunicode" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "850878694b7933ca4c9569d30a34b55031b9b139ee1fc7b94a527c4ef960d690" + +[[package]] name = "digest" version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -512,6 +618,23 @@ dependencies = [ ] [[package]] +name = "displaydoc" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.2", +] + +[[package]] +name = "dlv-list" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0688c2a7f92e427f44895cd63841bff7b29f8d7a1648b9e7e07a4a365b2e1257" + +[[package]] name = "dotenv" version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -652,6 +775,54 @@ dependencies = [ ] [[package]] +name = "globset" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "029d74589adefde59de1a0c4f4732695c32805624aec7b68d91503d4dba79afc" +dependencies = [ + "aho-corasick", + "bstr", + "fnv", + "log", + "regex", +] + +[[package]] +name = "globwalk" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93e3af942408868f6934a7b85134a3230832b9977cf66125df2f9edcfce4ddcc" +dependencies = [ + "bitflags", + "ignore", + "walkdir", +] + +[[package]] +name = "grass" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85cc4b64880a2264a41f9eab431780e72a68a6c88b9bddef361ba638812d572e" +dependencies = [ + "clap", + "grass_compiler", +] + +[[package]] +name = "grass_compiler" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e4feeef87d958eebd4d55431040768b93a5b088202198e0b203adc3c1d468c6" +dependencies = [ + "codemap", + "indexmap", + "lasso", + "once_cell", + "phf", + "rand", +] + +[[package]] name = "h2" version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -672,6 +843,15 @@ dependencies = [ [[package]] name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" +dependencies = [ + "ahash 0.7.6", +] + +[[package]] +name = "hashbrown" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" @@ -685,7 +865,7 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69fe1fcf8b4278d860ad0548329f892a3631fb63f82574df68275f34cdbe0ffa" dependencies = [ - "hashbrown", + "hashbrown 0.12.3", ] [[package]] @@ -699,6 +879,15 @@ dependencies = [ [[package]] name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "hermit-abi" version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" @@ -736,6 +925,38 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" [[package]] +name = "humansize" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6cb51c9a029ddc91b07a787f1d86b53ccfa49b0e86688c946ebe8d3555685dd7" +dependencies = [ + "libm", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0722cd7114b7de04316e7ea5456a0bbb20e4adb46fd27a3697adb812cff0f37c" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] name = "idna" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -746,13 +967,30 @@ dependencies = [ ] [[package]] +name = "ignore" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbe7873dab538a9a44ad79ede1faf5f30d49f9a5c883ddbab48bce81b64b7492" +dependencies = [ + "globset", + "lazy_static", + "log", + "memchr", + "regex", + "same-file", + "thread_local", + "walkdir", + "winapi-util", +] + +[[package]] name = "indexmap" version = "1.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" dependencies = [ "autocfg", - "hashbrown", + "hashbrown 0.12.3", ] [[package]] @@ -804,6 +1042,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388" [[package]] +name = "lasso" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aeb7b21a526375c5ca55f1a6dfd4e1fad9fa4edd750f530252a718a44b2608f0" +dependencies = [ + "hashbrown 0.11.2", +] + +[[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -975,7 +1222,7 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" dependencies = [ - "hermit-abi", + "hermit-abi 0.2.6", "libc", ] @@ -986,6 +1233,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" [[package]] +name = "ordered-multimap" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccd746e37177e1711c20dd619a1620f34f5c8b569c53590a72dedd5344d8924a" +dependencies = [ + "dlv-list", + "hashbrown 0.12.3", +] + +[[package]] name = "parking_lot" version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1034,6 +1291,15 @@ dependencies = [ ] [[package]] +name = "parse-zoneinfo" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c705f256449c60da65e11ff6626e0c16a0a0b96aaa348de61376b249bc340f41" +dependencies = [ + "regex", +] + +[[package]] name = "paste" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1055,6 +1321,105 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" [[package]] +name = "pest" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e68e84bfb01f0507134eac1e9b410a12ba379d064eab48c50ba4ce329a527b70" +dependencies = [ + "thiserror", + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b79d4c71c865a25a4322296122e3924d30bc8ee0834c8bfc8b95f7f054afbfb" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c435bf1076437b851ebc8edc3a18442796b30f1728ffea6262d59bbe28b077e" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn 2.0.2", +] + +[[package]] +name = "pest_meta" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "745a452f8eb71e39ffd8ee32b3c5f51d03845f99786fa9b68db6ff509c505411" +dependencies = [ + "once_cell", + "pest", + "sha2", +] + +[[package]] +name = "phf" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259" +dependencies = [ + "phf_macros", + "phf_shared", + "proc-macro-hack", +] + +[[package]] +name = "phf_codegen" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd" +dependencies = [ + "phf_generator", + "phf_shared", +] + +[[package]] +name = "phf_generator" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6" +dependencies = [ + "phf_shared", + "rand", +] + +[[package]] +name = "phf_macros" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58fdf3184dd560f160dd73922bea2d5cd6e8f064bf4b13110abd81b03697b4e0" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro-hack", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "phf_shared" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" +dependencies = [ + "siphasher", + "uncased", +] + +[[package]] name = "pin-project-lite" version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1101,6 +1466,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] +name = "proc-macro-hack" +version = "0.5.20+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" + +[[package]] name = "proc-macro2" version = "1.0.52" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1228,20 +1599,35 @@ dependencies = [ ] [[package]] +name = "rust-ini" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6d5f2436026b4f6e79dc829837d467cc7e9a55ee40e750d716713540715a2df" +dependencies = [ + "cfg-if", + "ordered-multimap", +] + +[[package]] name = "rust-pw-server" version = "0.1.0" dependencies = [ "actix-web", "dotenv", "exun", + "grass", "hex", "log", + "parking_lot 0.12.1", "raise", "rand", "rust-argon2", + "rust-ini", "serde", "sqlx", + "tera", "thiserror", + "unic-langid", "uuid", ] @@ -1282,6 +1668,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" [[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] name = "scopeguard" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1378,6 +1773,12 @@ dependencies = [ ] [[package]] +name = "siphasher" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" + +[[package]] name = "slab" version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1387,6 +1788,15 @@ dependencies = [ ] [[package]] +name = "slug" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3bc762e6a4b6c6fcaade73e77f9ebc6991b676f88bb2358bddb56560f073373" +dependencies = [ + "deunicode", +] + +[[package]] name = "smallvec" version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1534,6 +1944,12 @@ dependencies = [ ] [[package]] +name = "strsim" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" + +[[package]] name = "subtle" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1562,6 +1978,38 @@ dependencies = [ ] [[package]] +name = "tera" +version = "1.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95a665751302f22a03c56721e23094e4dc22b04a80f381e6737a07bf7a7c70c0" +dependencies = [ + "chrono", + "chrono-tz", + "globwalk", + "humansize", + "lazy_static", + "percent-encoding", + "pest", + "pest_derive", + "rand", + "regex", + "serde", + "serde_json", + "slug", + "thread_local", + "unic-segment", +] + +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", +] + +[[package]] name = "thiserror" version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1582,6 +2030,15 @@ dependencies = [ ] [[package]] +name = "thread_local" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180" +dependencies = [ + "once_cell", +] + +[[package]] name = "time" version = "0.3.20" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1609,6 +2066,15 @@ dependencies = [ ] [[package]] +name = "tinystr" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ac3f5b6856e931e15e07b478e98c8045239829a65f9156d4fa7e7788197a5ef" +dependencies = [ + "displaydoc", +] + +[[package]] name = "tinyvec" version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1706,6 +2172,90 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" [[package]] +name = "ucd-trie" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81" + +[[package]] +name = "uncased" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b9bc53168a4be7402ab86c3aad243a84dd7381d09be0eddc81280c1da95ca68" +dependencies = [ + "version_check", +] + +[[package]] +name = "unic-char-property" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221" +dependencies = [ + "unic-char-range", +] + +[[package]] +name = "unic-char-range" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc" + +[[package]] +name = "unic-common" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc" + +[[package]] +name = "unic-langid" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "398f9ad7239db44fd0f80fe068d12ff22d78354080332a5077dc6f52f14dcf2f" +dependencies = [ + "unic-langid-impl", +] + +[[package]] +name = "unic-langid-impl" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e35bfd2f2b8796545b55d7d3fd3e89a0613f68a0d1c8bc28cb7ff96b411a35ff" +dependencies = [ + "serde", + "tinystr", +] + +[[package]] +name = "unic-segment" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4ed5d26be57f84f176157270c112ef57b86debac9cd21daaabbe56db0f88f23" +dependencies = [ + "unic-ucd-segment", +] + +[[package]] +name = "unic-ucd-segment" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2079c122a62205b421f499da10f3ee0f7697f012f55b675e002483c73ea34700" +dependencies = [ + "unic-char-property", + "unic-char-range", + "unic-ucd-version", +] + +[[package]] +name = "unic-ucd-version" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4" +dependencies = [ + "unic-common", +] + +[[package]] name = "unicode-bidi" version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1733,6 +2283,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" [[package]] +name = "unicode-width" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" + +[[package]] name = "unicode_categories" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1767,12 +2323,28 @@ dependencies = [ ] [[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + +[[package]] name = "version_check" version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] +name = "walkdir" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1878,18 +2450,36 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] +name = "windows" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +dependencies = [ + "windows-targets 0.48.0", +] + +[[package]] name = "windows-sys" version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" dependencies = [ - "windows-targets", + "windows-targets 0.42.2", ] [[package]] @@ -1898,13 +2488,28 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.42.2", + "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 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +dependencies = [ + "windows_aarch64_gnullvm 0.48.0", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm 0.48.0", + "windows_x86_64_msvc 0.48.0", ] [[package]] @@ -1914,42 +2519,84 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" [[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + +[[package]] name = "windows_aarch64_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" [[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + +[[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_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + +[[package]] name = "windows_i686_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" [[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + +[[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_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + +[[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_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + +[[package]] name = "windows_x86_64_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" [[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" + +[[package]] name = "zeroize" version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -7,14 +7,19 @@ edition = "2021" [dependencies] actix-web = "4" +tera = "1" serde = "1" thiserror = "1" rust-argon2 = "1" uuid = { version = "1", features = [ "v4", "fast-rng", "serde" ] } raise = "2" +exun = "0.1" +rust-ini = "0.18" dotenv = "0.15" +parking_lot = "0.12" +grass = "0.12" +unic-langid = { version = "0.9", features = ["serde"] } rand = "0.8" sqlx = { version = "0.6", features = [ "runtime-actix-rustls", "mysql", "uuid", "offline" ] } log = "0.4" hex = "0.4" -exun = "0.1" diff --git a/src/api/liveops.rs b/src/api/liveops.rs index 2dda41d..d4bf129 100644 --- a/src/api/liveops.rs +++ b/src/api/liveops.rs @@ -1,11 +1,11 @@ use actix_web::{get, web, HttpResponse, Scope}; /// Simple ping -#[get("ping")] +#[get("/ping")] async fn ping() -> HttpResponse { HttpResponse::Ok().finish() } pub fn service() -> Scope { - web::scope("liveops").service(ping) + web::scope("/liveops").service(ping) } diff --git a/src/api/ops.rs b/src/api/ops.rs index 018743c..d947e64 100644 --- a/src/api/ops.rs +++ b/src/api/ops.rs @@ -1,9 +1,14 @@ -use actix_web::{http::StatusCode, post, web, HttpResponse, ResponseError, Scope}; +use std::str::FromStr; + +use actix_web::{get, http::StatusCode, post, web, HttpResponse, ResponseError, Scope}; use raise::yeet; use serde::Deserialize; use sqlx::MySqlPool; +use tera::Tera; use thiserror::Error; +use unic_langid::subtags::Language; +use crate::resources::{languages, templates}; use crate::services::db; /// A request to login @@ -60,6 +65,17 @@ async fn login( Ok(response) } +#[get("/login")] +async fn login_page( + tera: web::Data<Tera>, + translations: web::Data<languages::Translations>, +) -> HttpResponse { + // TODO find a better way of doing this + let language = Language::from_str("en").unwrap(); + let page = templates::login_page(&tera, language, translations.get_ref().clone()).unwrap(); + HttpResponse::Ok().content_type("text/html").body(page) +} + pub fn service() -> Scope { - web::scope("").service(login) + web::scope("").service(login).service(login_page) } diff --git a/src/api/users.rs b/src/api/users.rs index 863d99e..353f8ff 100644 --- a/src/api/users.rs +++ b/src/api/users.rs @@ -26,19 +26,21 @@ impl From<User> for UserResponse { } } -#[get("/")] -async fn search_users( - web::Query(username): web::Query<Option<Box<str>>>, - web::Query(limit): web::Query<Option<u32>>, - web::Query(offset): web::Query<Option<u32>>, - conn: web::Data<MySqlPool>, -) -> HttpResponse { +#[derive(Debug, Clone, Deserialize)] +struct SearchUsers { + username: Option<Box<str>>, + limit: Option<u32>, + offset: Option<u32>, +} + +#[get("")] +async fn search_users(params: web::Query<SearchUsers>, conn: web::Data<MySqlPool>) -> HttpResponse { let conn = conn.get_ref(); - let username = username.unwrap_or_default(); - let offset = offset.unwrap_or_default(); + let username = params.username.clone().unwrap_or_default(); + let offset = params.offset.unwrap_or_default(); - let results: Box<[UserResponse]> = if let Some(limit) = limit { + let results: Box<[UserResponse]> = if let Some(limit) = params.limit { db::search_users_limit(conn, &username, offset, limit) .await .unwrap() @@ -129,7 +131,7 @@ impl ResponseError for UsernameTakenError { } } -#[post("/")] +#[post("")] async fn create_user( body: web::Json<UserRequest>, conn: web::Data<MySqlPool>, diff --git a/src/main.rs b/src/main.rs index 7d2b643..7b25dd1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,5 @@ use actix_web::http::header::{self, HeaderValue}; -use actix_web::middleware::{DefaultHeaders, ErrorHandlerResponse, ErrorHandlers, Logger}; +use actix_web::middleware::{ErrorHandlerResponse, ErrorHandlers, Logger, NormalizePath}; use actix_web::web::Data; use actix_web::{dev, App, HttpServer}; @@ -7,8 +7,10 @@ use exun::*; mod api; mod models; +mod resources; mod services; +use resources::*; use services::*; fn error_content_language<B>( @@ -31,12 +33,27 @@ async fn main() -> Result<(), RawUnexpected> { let db_url = secrets::database_url()?; let sql_pool = db::initialize(&db_url).await?; + let tera = templates::initialize()?; + + let translations = languages::initialize()?; + // start the server HttpServer::new(move || { App::new() + // middleware .wrap(ErrorHandlers::new().default_handler(error_content_language)) + .wrap(NormalizePath::trim()) .wrap(Logger::new("\"%r\" %s %Dms")) + // app shared state .app_data(Data::new(sql_pool.clone())) + .app_data(Data::new(tera.clone())) + .app_data(Data::new(translations.clone())) + // frontend services + // has to be first so they don't get overwritten by the "" scope + .service(style::get_css) + .service(scripts::get_js) + .service(languages::languages()) + // api services .service(api::liveops()) .service(api::users()) .service(api::ops()) diff --git a/src/resources/languages.rs b/src/resources/languages.rs new file mode 100644 index 0000000..8ef7553 --- /dev/null +++ b/src/resources/languages.rs @@ -0,0 +1,67 @@ +use std::collections::HashMap; +use std::path::PathBuf; + +use actix_web::{get, web, HttpResponse, Scope}; +use exun::RawUnexpected; +use ini::{Ini, Properties}; +use raise::yeet; +use unic_langid::subtags::Language; + +#[derive(Debug, Clone, PartialEq)] +pub struct Translations { + languages: HashMap<Language, Properties>, +} + +pub fn initialize() -> Result<Translations, RawUnexpected> { + let mut translations = Translations { + languages: HashMap::new(), + }; + translations.refresh()?; + Ok(translations) +} + +impl Translations { + pub fn languages(&self) -> Box<[Language]> { + self.languages.keys().cloned().collect() + } + + pub fn get_message(&self, language: Language, key: &str) -> Option<String> { + Some(self.languages.get(&language)?.get(key)?.to_owned()) + } + + pub fn refresh(&mut self) -> Result<(), RawUnexpected> { + let mut languages = HashMap::with_capacity(1); + for entry in PathBuf::from("static/languages").read_dir()? { + let entry = entry?; + if entry.file_type()?.is_dir() { + continue; + } + + let path = entry.path(); + let path = path.to_string_lossy(); + let Some(language) = path.as_bytes().get(0..2) else { yeet!(RawUnexpected::msg(format!("{} not long enough to be a language name", path))) }; + let language = Language::from_bytes(language)?; + let messages = Ini::load_from_file(entry.path())?.general_section().clone(); + + languages.insert(language, messages); + } + + self.languages = languages; + Ok(()) + } +} + +#[get("")] +pub async fn all_languages(translations: web::Data<Translations>) -> HttpResponse { + HttpResponse::Ok().json( + translations + .languages() + .into_iter() + .map(|l| l.as_str()) + .collect::<Box<[&str]>>(), + ) +} + +pub fn languages() -> Scope { + web::scope("/languages").service(all_languages) +} diff --git a/src/resources/mod.rs b/src/resources/mod.rs new file mode 100644 index 0000000..9251d2c --- /dev/null +++ b/src/resources/mod.rs @@ -0,0 +1,4 @@ +pub mod languages; +pub mod scripts; +pub mod style; +pub mod templates; diff --git a/src/resources/scripts.rs b/src/resources/scripts.rs new file mode 100644 index 0000000..3e2d869 --- /dev/null +++ b/src/resources/scripts.rs @@ -0,0 +1,37 @@ +use std::path::{Path, PathBuf}; + +use actix_web::{get, http::StatusCode, web, HttpResponse, ResponseError}; +use exun::{Expect, ResultErrorExt}; +use raise::yeet; +use serde::Serialize; +use thiserror::Error; + +#[derive(Debug, Clone, Error, Serialize)] +pub enum LoadScriptError { + #[error("The requested script does not exist")] + FileNotFound(Box<Path>), +} + +impl ResponseError for LoadScriptError { + fn status_code(&self) -> StatusCode { + match self { + Self::FileNotFound(..) => StatusCode::NOT_FOUND, + } + } +} + +fn load(script: &str) -> Result<String, Expect<LoadScriptError>> { + let path = PathBuf::from(format!("static/scripts/{}.js", script)); + if !path.exists() { + yeet!(LoadScriptError::FileNotFound(path.into()).into()); + } + let js = std::fs::read_to_string(format!("static/scripts/{}.js", script)).unexpect()?; + Ok(js) +} + +#[get("/{script}.js")] +pub async fn get_js(script: web::Path<Box<str>>) -> Result<HttpResponse, LoadScriptError> { + let js = load(&script).map_err(|e| e.unwrap())?; + let response = HttpResponse::Ok().content_type("text/javascript").body(js); + Ok(response) +} diff --git a/src/resources/style.rs b/src/resources/style.rs new file mode 100644 index 0000000..2777a82 --- /dev/null +++ b/src/resources/style.rs @@ -0,0 +1,53 @@ +use std::path::{Path, PathBuf}; + +use actix_web::{get, http::StatusCode, web, HttpResponse, ResponseError}; +use exun::{Expect, ResultErrorExt}; +use grass::OutputStyle; +use raise::yeet; +use serde::Serialize; +use thiserror::Error; + +fn output_style() -> OutputStyle { + if cfg!(debug_assertions) { + OutputStyle::Expanded + } else { + OutputStyle::Compressed + } +} + +fn options() -> grass::Options<'static> { + grass::Options::default() + .load_path("static/style") + .style(output_style()) +} + +#[derive(Debug, Clone, Error, Serialize)] +pub enum LoadStyleError { + #[error("The requested stylesheet was not found")] + FileNotFound(Box<Path>), +} + +impl ResponseError for LoadStyleError { + fn status_code(&self) -> StatusCode { + match self { + Self::FileNotFound(..) => StatusCode::NOT_FOUND, + } + } +} + +pub fn load(stylesheet: &str) -> Result<String, Expect<LoadStyleError>> { + let options = options(); + let path = PathBuf::from(format!("static/style/{}.scss", stylesheet)); + if !path.exists() { + yeet!(LoadStyleError::FileNotFound(path.into()).into()); + } + let css = grass::from_path(format!("static/style/{}.scss", stylesheet), &options).unexpect()?; + Ok(css) +} + +#[get("/{stylesheet}.css")] +pub async fn get_css(stylesheet: web::Path<Box<str>>) -> Result<HttpResponse, LoadStyleError> { + let css = load(&stylesheet).map_err(|e| e.unwrap())?; + let response = HttpResponse::Ok().content_type("text/css").body(css); + Ok(response) +} diff --git a/src/resources/templates.rs b/src/resources/templates.rs new file mode 100644 index 0000000..43d6b67 --- /dev/null +++ b/src/resources/templates.rs @@ -0,0 +1,59 @@ +use std::collections::HashMap; + +use exun::{RawUnexpected, ResultErrorExt}; +use raise::yeet; +use tera::{Function, Tera, Value}; +use unic_langid::subtags::Language; + +use super::languages; + +fn make_lang(language: Language) -> impl Function { + Box::new(move |_: &HashMap<String, Value>| -> tera::Result<Value> { + Ok(Value::String(language.to_string())) + }) +} + +fn make_msg(language: Language, translations: languages::Translations) -> impl Function { + Box::new( + move |args: &HashMap<String, Value>| -> tera::Result<Value> { + let Some(key) = args.get("key") else { yeet!("No parameter 'key' provided".into()) }; + let Some(key) = key.as_str() else { yeet!(format!("{} is not a string", key).into()) }; + let Some(value) = translations.get_message(language, key) else { yeet!(format!("{} does not exist", key).into()) }; + Ok(Value::String(value)) + }, + ) +} + +fn make_base_url() -> impl Function { + Box::new(|_: &HashMap<String, Value>| Ok(Value::String("foo".to_string()))) +} + +fn extend_tera( + tera: &Tera, + language: Language, + translations: languages::Translations, +) -> Result<Tera, RawUnexpected> { + let mut new_tera = initialize()?; + new_tera.extend(tera)?; + new_tera.register_function("lang", make_lang(language)); + new_tera.register_function("msg", make_msg(language, translations)); + new_tera.register_function("baseUrl", make_base_url()); + Ok(new_tera) +} + +pub fn initialize() -> tera::Result<Tera> { + let tera = Tera::new("static/templates/*")?; + Ok(tera) +} + +pub fn login_page( + tera: &Tera, + language: Language, + mut translations: languages::Translations, +) -> Result<String, RawUnexpected> { + translations.refresh()?; + let mut tera = extend_tera(tera, language, translations)?; + tera.full_reload()?; + let context = tera::Context::new(); + tera.render("login.html", &context).unexpect() +} diff --git a/static/languages/en.ini b/static/languages/en.ini new file mode 100644 index 0000000..1848a7d --- /dev/null +++ b/static/languages/en.ini @@ -0,0 +1,6 @@ +loginTitle = Log In +usernameLabel = Username +usernamePlaceholder = Enter your username +passwordLabel = Password +passwordPlaceholder = Enter your password +loginSubmitButton = Log In diff --git a/static/scripts/main.js b/static/scripts/main.js new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/static/scripts/main.js diff --git a/static/style/style.scss b/static/style/style.scss new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/static/style/style.scss diff --git a/static/templates/base.html b/static/templates/base.html new file mode 100644 index 0000000..2d9f8fd --- /dev/null +++ b/static/templates/base.html @@ -0,0 +1,16 @@ +<!DOCTYPE html> +<html lang="{{lang()}}"> + <head> + <title>{% block title %}{% endblock title %}</title> + <meta charset="utf-8" /> + <meta name="viewport" content="width=device-width, initial-scale=1" /> + <link rel="stylesheet" href="/style.css" /> + {% block head %}{% endblock head %} + </head> + <body> + {% block content %}{% endblock content %} + <footer> + <div id="copyright">© 2023 Amplify Education Inc.</div> + </footer> + </body> +</html> diff --git a/static/templates/login.html b/static/templates/login.html new file mode 100644 index 0000000..fd3d136 --- /dev/null +++ b/static/templates/login.html @@ -0,0 +1,11 @@ +{% extends "base.html" %} +{% block title %}Log In{% endblock title %} +{% block content %} +<form method="post" action="{{ baseUrl() }}/authenticate"> + <label for="username">Username</label> + <input id="username" type="text" name="username" tabindex="0" placeholder="Enter your username" autofocus autocomplete="off" /> + <label for="password">Password</label> + <input id="password" type="password" name="password" tabindex="0" placeholder="Enter your password" autofocus autocomplete="off" /> + <input type="submit" tabindex="0" name="login" value="Log In" /> +</form> +{% endblock content %}
\ No newline at end of file |
