Merge pull request 'initial-frontend-work' (#2) from initial-frontend-work into main

Reviewed-on: #2
This commit is contained in:
Roflin 2022-05-27 20:30:06 +02:00
commit 5ffeea6553
18 changed files with 5290 additions and 4458 deletions

548
backend/Cargo.lock generated
View File

@ -2,6 +2,41 @@
# It is not intended for manual editing.
version = 3
[[package]]
name = "aead"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b613b8e1e3cf911a086f53f03bf286f52fd7a7258e4fa606f0ef220d39d8877"
dependencies = [
"generic-array 0.14.5",
]
[[package]]
name = "aes"
version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8"
dependencies = [
"cfg-if 1.0.0",
"cipher",
"cpufeatures",
"opaque-debug 0.3.0",
]
[[package]]
name = "aes-gcm"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df5f85a83a7d8b0442b6aa7b504b8212c1733da07b98aae43d4bc21b2cb3cdf6"
dependencies = [
"aead",
"aes",
"cipher",
"ctr",
"ghash",
"subtle",
]
[[package]]
name = "aho-corasick"
version = "0.7.18"
@ -89,12 +124,6 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "base-x"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4521f3e3d031370679b3b140beb36dfe4801b09ac77e30c61941f97df3ef28b"
[[package]]
name = "base64"
version = "0.13.0"
@ -214,22 +243,47 @@ dependencies = [
]
[[package]]
name = "const_fn"
version = "0.4.9"
name = "cipher"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbdcdcb6d86f71c5e97409ad45898af11cbc995b4ee8112d59095a28d376c935"
checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7"
dependencies = [
"generic-array 0.14.5",
]
[[package]]
name = "const-sha1"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb58b6451e8c2a812ad979ed1d83378caa5e927eef2622017a45f251457c2c9d"
[[package]]
name = "cookie"
version = "0.15.1"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d5f1c7727e460397e56abc4bddc1d49e07a1ad78fc98eb2e1c8f032a58a2f80d"
checksum = "94d4706de1b0fa5b132270cddffa8585166037822e260a944fe161acd137ca05"
dependencies = [
"aes-gcm",
"base64",
"hkdf",
"hmac",
"percent-encoding",
"time 0.2.27",
"rand",
"sha2",
"subtle",
"time 0.3.9",
"version_check",
]
[[package]]
name = "cpufeatures"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59a6001667ab124aebae2a495118e11d30984c3a653e99d86d58971708cf5e4b"
dependencies = [
"libc",
]
[[package]]
name = "crypto-common"
version = "0.1.3"
@ -240,6 +294,15 @@ dependencies = [
"typenum",
]
[[package]]
name = "ctr"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "049bb91fb4aaf0e3c7efa6cd5ef877dbbbd15b39dad06d9948de4ec8a75761ea"
dependencies = [
"cipher",
]
[[package]]
name = "devise"
version = "0.3.1"
@ -338,12 +401,6 @@ dependencies = [
"subtle",
]
[[package]]
name = "discard"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0"
[[package]]
name = "either"
version = "1.6.1"
@ -459,7 +516,6 @@ checksum = "f73fe65f54d1e12b726f517d3e2135ca3125a437b6d998caf1962961f7172d9e"
dependencies = [
"futures-channel",
"futures-core",
"futures-executor",
"futures-io",
"futures-sink",
"futures-task",
@ -482,34 +538,12 @@ version = "0.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3"
[[package]]
name = "futures-executor"
version = "0.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9420b90cfa29e327d0429f19be13e7ddb68fa1cccb09d65e5706b8c7a749b8a6"
dependencies = [
"futures-core",
"futures-task",
"futures-util",
]
[[package]]
name = "futures-io"
version = "0.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc4045962a5a5e935ee2fdedaa4e08284547402885ab326734432bed5d12966b"
[[package]]
name = "futures-macro"
version = "0.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33c1e13800337f4d4d7a316bf45a567dbcb6ffe087f16424852d97e97a91f512"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "futures-sink"
version = "0.3.21"
@ -531,7 +565,6 @@ dependencies = [
"futures-channel",
"futures-core",
"futures-io",
"futures-macro",
"futures-sink",
"futures-task",
"memchr",
@ -551,9 +584,11 @@ dependencies = [
"diesel_migrations",
"jsonwebtoken",
"libsqlite3-sys",
"local-ip-address",
"password-hash",
"rand_core",
"rocket",
"rocket_cors",
"rocket_dyn_templates",
"rocket_sync_db_pools",
"serde",
@ -603,6 +638,16 @@ dependencies = [
"wasi 0.10.0+wasi-snapshot-preview1",
]
[[package]]
name = "ghash"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1583cc1656d7839fd3732b80cf4f38850336cdb9b8ded1cd399ca62958de3c99"
dependencies = [
"opaque-debug 0.3.0",
"polyval",
]
[[package]]
name = "glob"
version = "0.3.0"
@ -624,15 +669,15 @@ dependencies = [
"indexmap",
"slab",
"tokio",
"tokio-util",
"tokio-util 0.6.9",
"tracing",
]
[[package]]
name = "handlebars"
version = "3.5.5"
version = "4.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4498fc115fa7d34de968184e473529abb40eeb6be8bc5f7faba3d08c316cb3e3"
checksum = "99d6a30320f094710245150395bc763ad23128d6a1ebbad7594dc4164b62c56b"
dependencies = [
"log",
"pest",
@ -663,6 +708,24 @@ dependencies = [
"libc",
]
[[package]]
name = "hkdf"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437"
dependencies = [
"hmac",
]
[[package]]
name = "hmac"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
dependencies = [
"digest 0.10.3",
]
[[package]]
name = "http"
version = "0.2.6"
@ -861,6 +924,19 @@ dependencies = [
"vcpkg",
]
[[package]]
name = "local-ip-address"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b143c6ef86e36328caa40a7578e95d1544aca8a1740235fd2b416a69441a5c7"
dependencies = [
"libc",
"memalloc",
"neli",
"thiserror",
"windows",
]
[[package]]
name = "lock_api"
version = "0.4.6"
@ -915,6 +991,12 @@ version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f"
[[package]]
name = "memalloc"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df39d232f5c40b0891c10216992c2f250c054105cb1e56f0fc9032db6203ecc1"
[[package]]
name = "memchr"
version = "2.4.1"
@ -1030,10 +1112,20 @@ dependencies = [
"mime",
"spin 0.9.2",
"tokio",
"tokio-util",
"tokio-util 0.6.9",
"version_check",
]
[[package]]
name = "neli"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9053554eb5dcb7e10d9cdab1206965bde870eed5d0d341532ca035e3ba221508"
dependencies = [
"byteorder",
"libc",
]
[[package]]
name = "net2"
version = "0.2.37"
@ -1142,6 +1234,12 @@ version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c"
[[package]]
name = "opaque-debug"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
[[package]]
name = "parking_lot"
version = "0.11.2"
@ -1150,7 +1248,17 @@ checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99"
dependencies = [
"instant",
"lock_api",
"parking_lot_core",
"parking_lot_core 0.8.5",
]
[[package]]
name = "parking_lot"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87f5ec2493a61ac0506c0f4199f99070cbe83857b0337006a30f3e6719b8ef58"
dependencies = [
"lock_api",
"parking_lot_core 0.9.3",
]
[[package]]
@ -1167,6 +1275,19 @@ dependencies = [
"winapi 0.3.9",
]
[[package]]
name = "parking_lot_core"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929"
dependencies = [
"cfg-if 1.0.0",
"libc",
"redox_syscall",
"smallvec",
"windows-sys",
]
[[package]]
name = "password-hash"
version = "0.4.0"
@ -1277,6 +1398,18 @@ version = "0.3.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "58893f751c9b0412871a09abd62ecd2a00298c6c83befa223ef98c52aef40cbe"
[[package]]
name = "polyval"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8419d2b623c7c0896ff2d5d96e2cb4ede590fed28fcc34934f4c33c036e620a1"
dependencies = [
"cfg-if 1.0.0",
"cpufeatures",
"opaque-debug 0.3.0",
"universal-hash",
]
[[package]]
name = "ppv-lite86"
version = "0.2.16"
@ -1307,12 +1440,6 @@ dependencies = [
"version_check",
]
[[package]]
name = "proc-macro-hack"
version = "0.5.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5"
[[package]]
name = "proc-macro2"
version = "1.0.36"
@ -1366,7 +1493,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "545c5bc2b880973c9c10e4067418407a0ccaa3091781d1671d46eb35107cb26f"
dependencies = [
"log",
"parking_lot",
"parking_lot 0.11.2",
"scheduled-thread-pool",
]
@ -1481,9 +1608,9 @@ dependencies = [
[[package]]
name = "rocket"
version = "0.5.0-rc.1"
version = "0.5.0-rc.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a71c18c42a0eb15bf3816831caf0dad11e7966f2a41aaf486a701979c4dd1f2"
checksum = "98ead083fce4a405feb349cf09abdf64471c6077f14e0ce59364aa90d4b99317"
dependencies = [
"async-stream",
"async-trait",
@ -1499,7 +1626,7 @@ dependencies = [
"memchr",
"multer",
"num_cpus",
"parking_lot",
"parking_lot 0.12.0",
"pin-project-lite",
"rand",
"ref-cast",
@ -1509,10 +1636,10 @@ dependencies = [
"serde_json",
"state",
"tempfile",
"time 0.2.27",
"time 0.3.9",
"tokio",
"tokio-stream",
"tokio-util",
"tokio-util 0.7.1",
"ubyte",
"version_check",
"yansi",
@ -1520,9 +1647,9 @@ dependencies = [
[[package]]
name = "rocket_codegen"
version = "0.5.0-rc.1"
version = "0.5.0-rc.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "66f5fa462f7eb958bba8710c17c5d774bbbd59809fa76fb1957af7e545aea8bb"
checksum = "d6aeb6bb9c61e9cd2c00d70ea267bf36f76a4cc615e5908b349c2f9d93999b47"
dependencies = [
"devise",
"glob",
@ -1535,35 +1662,48 @@ dependencies = [
]
[[package]]
name = "rocket_dyn_templates"
version = "0.1.0-rc.1"
name = "rocket_cors"
version = "0.6.0-alpha1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c83f1287ad8fa034410928297a91db37518d5c46d7cc7e1e1b4a77aec0cd8807"
checksum = "3f5aa2c9cdb5dabbbf38bd4fb0038844cc47598399fed70fc938185679154793"
dependencies = [
"log",
"regex",
"rocket",
"serde",
"serde_derive",
"unicase",
"unicase_serde",
"url",
]
[[package]]
name = "rocket_dyn_templates"
version = "0.1.0-rc.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bab13df598440527c200f46fb944dc55d8d67a1818b617eb5a3981dcd8b63fd2"
dependencies = [
"glob",
"handlebars",
"normpath",
"notify",
"rocket",
"serde",
"serde_json",
]
[[package]]
name = "rocket_http"
version = "0.5.0-rc.1"
version = "0.5.0-rc.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23c8b7d512d2fcac2316ebe590cde67573844b99e6cc9ee0f53375fa16e25ebd"
checksum = "2ded65d127954de3c12471630bf4b81a2792f065984461e65b91d0fdaafc17a2"
dependencies = [
"cookie",
"either",
"futures",
"http",
"hyper",
"indexmap",
"log",
"memchr",
"mime",
"parking_lot",
"pear",
"percent-encoding",
"pin-project-lite",
@ -1572,16 +1712,16 @@ dependencies = [
"smallvec",
"stable-pattern",
"state",
"time 0.2.27",
"time 0.3.9",
"tokio",
"uncased",
]
[[package]]
name = "rocket_sync_db_pools"
version = "0.1.0-rc.1"
version = "0.1.0-rc.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38cfdfebd552d075c368e641c88a5cd6ce1c58c5c710548aeb777abb48830f4b"
checksum = "5fa48b6ab25013e9812f1b0c592741900b3a2a83c0936292e0565c0ac842f558"
dependencies = [
"diesel",
"r2d2",
@ -1593,23 +1733,14 @@ dependencies = [
[[package]]
name = "rocket_sync_db_pools_codegen"
version = "0.1.0-rc.1"
version = "0.1.0-rc.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5267808c094db5366e1d8925aaf9f2ce05ff9b3bd92cb18c7040a1fe219c2e25"
checksum = "280ef2d232923e69cb93da156972eb5476a7cce5ba44843f6608f46a4abf7aab"
dependencies = [
"devise",
"quote",
]
[[package]]
name = "rustc_version"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
dependencies = [
"semver",
]
[[package]]
name = "rustversion"
version = "1.0.6"
@ -1637,7 +1768,7 @@ version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc6f74fd1204073fa02d5d5d68bec8021be4c38690b61264b2fdb48083d0e7d7"
dependencies = [
"parking_lot",
"parking_lot 0.11.2",
]
[[package]]
@ -1652,21 +1783,6 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "semver"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
dependencies = [
"semver-parser",
]
[[package]]
name = "semver-parser"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
[[package]]
name = "serde"
version = "1.0.136"
@ -1707,24 +1823,20 @@ dependencies = [
"block-buffer 0.7.3",
"digest 0.8.1",
"fake-simd",
"opaque-debug",
"opaque-debug 0.2.3",
]
[[package]]
name = "sha1"
version = "0.6.1"
name = "sha2"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1da05c97445caa12d05e848c4a4fcbbea29e748ac28f7e80e9b010392063770"
checksum = "55deaec60f81eefe3cce0dc50bda92d6d8e88f2a27df7c5033b42afeb1ed2676"
dependencies = [
"sha1_smol",
"cfg-if 1.0.0",
"cpufeatures",
"digest 0.10.3",
]
[[package]]
name = "sha1_smol"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012"
[[package]]
name = "sharded-slab"
version = "0.1.4"
@ -1798,73 +1910,15 @@ dependencies = [
"memchr",
]
[[package]]
name = "standback"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e113fb6f3de07a243d434a56ec6f186dfd51cb08448239fe7bcae73f87ff28ff"
dependencies = [
"version_check",
]
[[package]]
name = "state"
version = "0.5.2"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87cf4f5369e6d3044b5e365c9690f451516ac8f0954084622b49ea3fde2f6de5"
checksum = "dbe866e1e51e8260c9eed836a042a5e7f6726bb2b411dffeaa712e19c388f23b"
dependencies = [
"loom",
]
[[package]]
name = "stdweb"
version = "0.4.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d022496b16281348b52d0e30ae99e01a73d737b2f45d38fed4edf79f9325a1d5"
dependencies = [
"discard",
"rustc_version",
"stdweb-derive",
"stdweb-internal-macros",
"stdweb-internal-runtime",
"wasm-bindgen",
]
[[package]]
name = "stdweb-derive"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c87a60a40fccc84bef0652345bbbbbe20a605bf5d0ce81719fc476f5c03b50ef"
dependencies = [
"proc-macro2",
"quote",
"serde",
"serde_derive",
"syn",
]
[[package]]
name = "stdweb-internal-macros"
version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "58fa5ff6ad0d98d1ffa8cb115892b6e69d67799f6763e162a1c9db421dc22e11"
dependencies = [
"base-x",
"proc-macro2",
"quote",
"serde",
"serde_derive",
"serde_json",
"sha1",
"syn",
]
[[package]]
name = "stdweb-internal-runtime"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0"
[[package]]
name = "subtle"
version = "2.4.1"
@ -1936,21 +1990,6 @@ dependencies = [
"winapi 0.3.9",
]
[[package]]
name = "time"
version = "0.2.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4752a97f8eebd6854ff91f1c1824cd6160626ac4bd44287f7f4ea2035a02a242"
dependencies = [
"const_fn",
"libc",
"standback",
"stdweb",
"time-macros 0.1.1",
"version_check",
"winapi 0.3.9",
]
[[package]]
name = "time"
version = "0.3.9"
@ -1961,17 +2000,7 @@ dependencies = [
"libc",
"num_threads",
"quickcheck",
"time-macros 0.2.4",
]
[[package]]
name = "time-macros"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "957e9c6e26f12cb6d0dd7fc776bb67a706312e7299aed74c8dd5b17ebb27e2f1"
dependencies = [
"proc-macro-hack",
"time-macros-impl",
"time-macros",
]
[[package]]
@ -1980,19 +2009,6 @@ version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42657b1a6f4d817cda8e7a0ace261fe0cc946cf3a80314390b22cc61ae080792"
[[package]]
name = "time-macros-impl"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd3c141a1b43194f3f56a1411225df8646c55781d5f26db825b3d98507eb482f"
dependencies = [
"proc-macro-hack",
"proc-macro2",
"quote",
"standback",
"syn",
]
[[package]]
name = "tinyvec"
version = "1.5.1"
@ -2063,6 +2079,19 @@ dependencies = [
"tokio",
]
[[package]]
name = "tokio-util"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0edfdeb067411dba2044da6d1cb2df793dd35add7888d73c16e3381ded401764"
dependencies = [
"bytes",
"futures-core",
"futures-sink",
"pin-project-lite",
"tokio",
]
[[package]]
name = "toml"
version = "0.5.8"
@ -2177,6 +2206,25 @@ dependencies = [
"version_check",
]
[[package]]
name = "unicase"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6"
dependencies = [
"version_check",
]
[[package]]
name = "unicase_serde"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ef53697679d874d69f3160af80bc28de12730a985d57bdf2b47456ccb8b11f1"
dependencies = [
"serde",
"unicase",
]
[[package]]
name = "unicode-bidi"
version = "0.3.7"
@ -2198,6 +2246,16 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
[[package]]
name = "universal-hash"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05"
dependencies = [
"generic-array 0.14.5",
"subtle",
]
[[package]]
name = "untrusted"
version = "0.7.1"
@ -2417,6 +2475,76 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows"
version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68088239696c06152844eadc03d262f088932cce50c67e4ace86e19d95e976fe"
dependencies = [
"const-sha1",
"windows_gen",
"windows_macros",
]
[[package]]
name = "windows-sys"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2"
dependencies = [
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_msvc"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47"
[[package]]
name = "windows_gen"
version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf583322dc423ee021035b358e535015f7fd163058a31e2d37b99a939141121d"
[[package]]
name = "windows_i686_gnu"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6"
[[package]]
name = "windows_i686_msvc"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024"
[[package]]
name = "windows_macros"
version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "58acfb8832e9f707f8997bd161e537a1c1f603e60a5bd9c3cf53484fdcc998f3"
dependencies = [
"syn",
"windows_gen",
]
[[package]]
name = "windows_x86_64_gnu"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1"
[[package]]
name = "windows_x86_64_msvc"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"
[[package]]
name = "ws2_32-sys"
version = "0.2.1"

View File

@ -7,12 +7,12 @@ edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
rocket = { version = "0.5.0-rc.1", features = ["default", "json"] }
rocket = { version = "0.5.0-rc.2", features = ["default", "json"] }
libsqlite3-sys = { version = ">=0.8.0, <0.19.0", features = ["bundled"] }
rocket_sync_db_pools = { version = "0.1.0-rc.1", features = ["diesel_sqlite_pool"] }
rocket_sync_db_pools = { version = "0.1.0-rc.2", features = ["diesel_sqlite_pool"] }
diesel = { version = "1.4.8", features = ["sqlite"] }
diesel_migrations = "1.4.0"
rocket_dyn_templates = { version = "0.1.0-rc.1", features = ["handlebars"] }
rocket_dyn_templates = { version = "0.1.0-rc.2", features = ["handlebars"] }
chrono = "0.4.19"
serde = "1.0.136"
password-hash = "0.4"
@ -21,4 +21,5 @@ rand_core = { version = "0.6", features = ["std"] }
diesel-derive-enum = { version = "1.1", features = ["sqlite"] }
jsonwebtoken = "8.1"
validator = { version = "0.14", features = ["derive"] }
rocket_cors = "0.6.0-alpha1"
local-ip-address = "0.4"

View File

@ -8,5 +8,12 @@ CREATE TABLE user (
CREATE TABLE pwd (
user_id INTEGER NOT NULL PRIMARY KEY,
password TEXT NOT NULL,
FOREIGN KEY (user_id) REFERENCES user(id) ON DELETE CASCADE
CONSTRAINT FK_UserId FOREIGN KEY (user_id) REFERENCES user(id) ON DELETE CASCADE
);
--Initialize default admin user, with password "gamenight!"
INSERT INTO user (id, username, role)
values(-1, 'admin', 'admin');
insert INTO pwd (id, pwd)
values(-1, '$argon2id$v=19$m=4096,t=3,p=1$zEdUjCAnZqd8DziYWzlFHw$YBLQhKvYIZBY43B8zM6hyBvLKuqTeh0EM5pKOfbWQSI');

View File

@ -0,0 +1,4 @@
-- This file should undo anything in `up.sql`
ALTER TABLE gamenight
DROP COLUMN owner_id;

View File

@ -0,0 +1,19 @@
ALTER TABLE gamenight RENAME TO _gamenight_old;
CREATE TABLE gamenight (
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
game text TEXT NOT NULL,
datetime TEXT NOT NULL,
owner_id INTEGER NOT NULL,
CONSTRAINT FK_OwnerId FOREIGN KEY (owner_id) REFERENCES user(id) ON DELETE CASCADE
);
PRAGMA foreign_keys=off;
INSERT INTO gamenight (id, game, datetime, owner_id)
select id, game, datetime, -1
FROM _gamenight_old;
drop table _gamenight_old;
PRAGMA foreign_keys=on;

View File

@ -24,13 +24,22 @@ pub enum ApiResponseVariant {
// Flash(Flash<Redirect>)
}
#[derive(Debug, Serialize, Deserialize)]
pub struct UserWithToken {
#[serde(flatten)]
pub user: schema::User,
pub jwt: String,
}
#[derive(Serialize, Deserialize, Debug)]
struct ApiResponse {
result: Cow<'static, str>,
#[serde(skip_serializing_if = "Option::is_none")]
message: Option<Cow<'static, str>>,
#[serde(skip_serializing_if = "Option::is_none")]
jwt: Option<Cow<'static, str>>,
user: Option<UserWithToken>,
#[serde(skip_serializing_if = "Option::is_none")]
gamenights: Option<Vec<schema::GameNight>>,
}
impl ApiResponse {
@ -40,22 +49,37 @@ impl ApiResponse {
const SUCCES: Self = Self {
result: Self::SUCCES_RESULT,
message: None,
jwt: None,
user: None,
gamenights: None,
};
fn error(message: String) -> Self {
Self {
result: Self::FAILURE_RESULT,
message: Some(Cow::Owned(message)),
jwt: None,
user: None,
gamenights: None,
}
}
fn login_response(jwt: String) -> Self {
fn login_response(user: schema::User, jwt: String) -> Self {
Self {
result: Self::SUCCES_RESULT,
message: None,
jwt: Some(Cow::Owned(jwt)),
user: Some(UserWithToken {
user: user,
jwt: jwt
}),
gamenights: None,
}
}
fn gamenight_response(gamenights: Vec<schema::GameNight>) -> Self {
Self {
result: Self::SUCCES_RESULT,
message: None,
user: None,
gamenights: Some(gamenights),
}
}
}
@ -99,14 +123,19 @@ impl<'r> FromRequest<'r> for schema::User {
let id = token.claims.uid;
let conn = req.guard::<DbConn>().await.unwrap();
return Outcome::Success(schema::get_user(conn, id).await);
return match schema::get_user(conn, id).await {
Ok(o) => Outcome::Success(o),
Err(_) => Outcome::Forward(())
}
}
}
#[get("/gamenights")]
pub async fn gamenights(conn: DbConn, _user: schema::User) -> ApiResponseVariant {
let gamenights = schema::get_all_gamenights(conn).await;
ApiResponseVariant::Value(json!(gamenights))
match schema::get_all_gamenights(conn).await {
Ok(gamenights) => ApiResponseVariant::Value(json!(ApiResponse::gamenight_response(gamenights))),
Err(error) => ApiResponseVariant::Value(json!(ApiResponse::error(error.to_string())))
}
}
#[get("/gamenights", rank = 2)]
@ -117,15 +146,53 @@ pub async fn gamenights_unauthorized() -> ApiResponseVariant {
#[post("/gamenight", format = "application/json", data = "<gamenight_json>")]
pub async fn gamenight_post_json(
conn: DbConn,
user: Option<schema::User>,
user: schema::User,
gamenight_json: Json<schema::GameNightNoId>,
) -> ApiResponseVariant {
if user.is_some() {
schema::insert_gamenight(conn, gamenight_json.into_inner()).await;
ApiResponseVariant::Value(json!(ApiResponse::SUCCES))
} else {
let mut gamenight = gamenight_json.into_inner();
gamenight.owner_id = Some(user.id);
match schema::insert_gamenight(conn, gamenight).await {
Ok(_) => ApiResponseVariant::Value(json!(ApiResponse::SUCCES)),
Err(err) => ApiResponseVariant::Value(json!(ApiResponse::error(err.to_string()))),
}
}
#[post("/gamenight", rank = 2)]
pub async fn gamenight_post_json_unauthorized() -> ApiResponseVariant {
ApiResponseVariant::Status(Status::Unauthorized)
}
#[delete("/gamenight", format = "application/json", data = "<delete_gamenight_json>")]
pub async fn gamenight_delete_json(
conn: DbConn,
user: schema::User,
delete_gamenight_json: Json<schema::DeleteGameNight>
) -> ApiResponseVariant {
if user.role == schema::Role::Admin {
if let Err(error) = schema::delete_gamenight(&conn, delete_gamenight_json.game_id).await {
return ApiResponseVariant::Value(json!(ApiResponse::error(error.to_string())))
}
return ApiResponseVariant::Value(json!(ApiResponse::SUCCES))
}
match schema::get_gamenight(&conn, delete_gamenight_json.game_id).await {
Ok(gamenight) => {
if user.id == gamenight.owner_id {
if let Err(error) = schema::delete_gamenight(&conn, delete_gamenight_json.game_id).await {
return ApiResponseVariant::Value(json!(ApiResponse::error(error.to_string())))
}
return ApiResponseVariant::Value(json!(ApiResponse::SUCCES))
}
},
Err(error) => return ApiResponseVariant::Value(json!(ApiResponse::error(error.to_string())))
}
ApiResponseVariant::Status(Status::Unauthorized)
}
#[delete("/gamenight", rank = 2)]
pub async fn gamenight_delete_json_unauthorized() -> ApiResponseVariant {
ApiResponseVariant::Status(Status::Unauthorized)
}
#[post("/register", format = "application/json", data = "<register_json>")]
@ -173,10 +240,11 @@ pub async fn login_post_json(
))));
}
let user = login_result.user.unwrap();
let my_claims = Claims {
exp: Utc::now().timestamp() + chrono::Duration::days(7).num_seconds(),
uid: login_result.id.unwrap(),
role: login_result.role.unwrap(),
uid: user.id,
role: user.role,
};
let secret = &config.inner().jwt_secret;
@ -185,7 +253,7 @@ pub async fn login_post_json(
&my_claims,
&EncodingKey::from_secret(secret.as_bytes()),
) {
Ok(token) => ApiResponseVariant::Value(json!(ApiResponse::login_response(token))),
Ok(token) => ApiResponseVariant::Value(json!(ApiResponse::login_response(user, token))),
Err(error) => {
ApiResponseVariant::Value(json!(ApiResponse::error(error.to_string())))
}

View File

@ -33,7 +33,7 @@ impl Default for AppConfig {
}
#[launch]
fn rocket() -> _ {
async fn rocket() -> _ {
let figment = Figment::from(rocket::Config::default())
.merge(Serialized::defaults(AppConfig::default()))
.merge(Toml::file("App.toml").nested())
@ -45,13 +45,12 @@ fn rocket() -> _ {
.attach(Template::fairing())
.attach(AdHoc::on_ignite("Run Migrations", schema::run_migrations))
.attach(AdHoc::config::<AppConfig>())
.attach(site::make_cors())
.mount(
"/",
routes![
site::index,
site::gamenights,
site::add_game_night,
site::register
site::files
],
)
.mount(
@ -60,8 +59,11 @@ fn rocket() -> _ {
api::gamenights,
api::gamenights_unauthorized,
api::gamenight_post_json,
api::gamenight_post_json_unauthorized,
api::register_post_json,
api::login_post_json
api::login_post_json,
api::gamenight_delete_json,
api::gamenight_delete_json_unauthorized
],
);

View File

@ -34,6 +34,7 @@ table! {
id -> Integer,
game -> Text,
datetime -> Text,
owner_id -> Integer,
}
}
@ -70,6 +71,18 @@ pub enum DatabaseError {
Query(String),
}
impl From<diesel::result::Error> for DatabaseError {
fn from(error: diesel::result::Error) -> Self {
Self::Query(error.to_string())
}
}
impl From<password_hash::Error> for DatabaseError {
fn from(error: password_hash::Error) -> Self {
Self::Hash(error)
}
}
impl std::fmt::Display for DatabaseError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
match self {
@ -79,33 +92,44 @@ impl std::fmt::Display for DatabaseError {
}
}
pub async fn get_all_gamenights(conn: DbConn) -> Vec<GameNight> {
conn.run(|c| gamenight::table.load::<GameNight>(c).unwrap())
.await
pub async fn get_all_gamenights(conn: DbConn) -> Result<Vec<GameNight>, DatabaseError> {
Ok(conn.run(|c|
gamenight::table.load::<GameNight>(c)
).await?)
}
pub async fn insert_gamenight(conn: DbConn, new_gamenight: GameNightNoId) -> () {
conn.run(|c| {
pub async fn insert_gamenight(conn: DbConn, new_gamenight: GameNightNoId) -> Result<usize, DatabaseError> {
Ok(conn.run(|c|
diesel::insert_into(gamenight::table)
.values(new_gamenight)
.execute(c)
.unwrap()
})
.await;
).await?)
}
pub async fn insert_user(conn: DbConn, new_user: Register) -> Result<(), DatabaseError> {
pub async fn get_gamenight(conn: &DbConn, game_id: i32) -> Result<GameNight, DatabaseError> {
Ok(conn.run(move |c|
gamenight::table.find(game_id).first(c)
).await?)
}
pub async fn delete_gamenight(conn: &DbConn, game_id: i32) -> Result<usize, DatabaseError> {
Ok(conn.run(move |c|
diesel::delete(
gamenight::table.filter(
gamenight::id.eq(game_id)
)
).execute(c)
).await?)
}
pub async fn insert_user(conn: DbConn, new_user: Register) -> Result<usize, DatabaseError> {
let salt = SaltString::generate(&mut OsRng);
let argon2 = Argon2::default();
let password_hash = match argon2.hash_password(new_user.password.as_bytes(), &salt) {
Ok(hash) => hash.to_string(),
Err(error) => return Err(DatabaseError::Hash(error)),
};
let password_hash = argon2.hash_password(new_user.password.as_bytes(), &salt)?.to_string();
let user_insert_result = conn
.run(move |c| {
Ok(conn.run(move |c| {
c.transaction(|| {
diesel::insert_into(user::table)
.values((
@ -115,90 +139,61 @@ pub async fn insert_user(conn: DbConn, new_user: Register) -> Result<(), Databas
))
.execute(c)?;
let ids: Vec<i32> = match user::table
let id: i32 = user::table
.filter(
user::username
.eq(&new_user.username)
.and(user::email.eq(&new_user.email)),
)
.select(user::id)
.get_results(c)
{
Ok(id) => id,
Err(e) => return Err(e),
};
.first(c)?;
diesel::insert_into(pwd::table)
.values((pwd::user_id.eq(ids[0]), pwd::password.eq(&password_hash)))
.values((pwd::user_id.eq(id), pwd::password.eq(&password_hash)))
.execute(c)
})
})
.await;
match user_insert_result {
Err(e) => Err(DatabaseError::Query(e.to_string())),
_ => Ok(()),
}
.await?)
}
pub async fn login(conn: DbConn, login: Login) -> Result<LoginResult, DatabaseError> {
conn.run(move |c| -> Result<LoginResult, DatabaseError> {
let id: i32 = match user::table
let id: i32 = user::table
.filter(user::username.eq(&login.username))
.or_filter(user::email.eq(&login.username))
.select(user::id)
.first(c)
{
Ok(id) => id,
Err(error) => return Err(DatabaseError::Query(error.to_string())),
};
.first(c)?;
let pwd: String = match pwd::table
let pwd: String = pwd::table
.filter(pwd::user_id.eq(id))
.select(pwd::password)
.first(c)
{
Ok(pwd) => pwd,
Err(error) => return Err(DatabaseError::Query(error.to_string())),
};
.first(c)?;
let parsed_hash = match PasswordHash::new(&pwd) {
Ok(hash) => hash,
Err(error) => return Err(DatabaseError::Hash(error)),
};
let parsed_hash = PasswordHash::new(&pwd)?;
if Argon2::default()
.verify_password(&login.password.as_bytes(), &parsed_hash)
.is_ok()
{
let role: Role = match user::table
.filter(user::id.eq(id))
.select(user::role)
.first(c)
{
Ok(role) => role,
Err(error) => return Err(DatabaseError::Query(error.to_string())),
};
let user: User = user::table.find(id).first(c)?;
Ok(LoginResult {
result: true,
id: Some(id),
role: Some(role),
user : Some(user)
})
} else {
Ok(LoginResult {
result: false,
id: None,
role: None,
user: None
})
}
})
.await
}
pub async fn get_user(conn: DbConn, id: i32) -> User {
conn.run(move |c| user::table.filter(user::id.eq(id)).first(c).unwrap())
.await
pub async fn get_user(conn: DbConn, id: i32) -> Result<User, DatabaseError> {
Ok(conn.run(move |c| user::table.filter(user::id.eq(id)).first(c))
.await?)
}
pub fn unique_username(
@ -247,7 +242,7 @@ pub async fn run_migrations(rocket: Rocket<Build>) -> Rocket<Build> {
rocket
}
#[derive(Debug, Serialize, Deserialize, DbEnum, Clone)]
#[derive(Debug, Serialize, Deserialize, DbEnum, Clone, Copy, PartialEq)]
pub enum Role {
Admin,
User,
@ -274,11 +269,12 @@ pub struct Game {
pub game: String,
}
#[derive(Serialize, Deserialize, Debug, FromForm, Insertable)]
#[derive(Serialize, Deserialize, Debug, Insertable)]
#[table_name = "gamenight"]
pub struct GameNightNoId {
pub game: String,
pub datetime: String,
pub owner_id: Option<i32>
}
#[derive(Serialize, Deserialize, Debug, FromForm, Queryable)]
@ -286,6 +282,12 @@ pub struct GameNight {
pub id: i32,
pub game: String,
pub datetime: String,
pub owner_id: i32,
}
#[derive(Serialize, Deserialize, Debug, FromForm, Queryable)]
pub struct DeleteGameNight {
pub game_id: i32,
}
#[derive(Serialize, Deserialize, Debug, Validate, Clone)]
@ -314,6 +316,5 @@ pub struct Login {
#[derive(Serialize, Deserialize, Debug)]
pub struct LoginResult {
pub result: bool,
pub id: Option<i32>,
pub role: Option<Role>,
pub user: Option<User>,
}

View File

@ -1,90 +1,43 @@
use crate::schema;
use rocket::request::FlashMessage;
use rocket::response::Redirect;
use rocket_dyn_templates::Template;
use serde::{Deserialize, Serialize};
use std::borrow::Cow;
use rocket::fs::NamedFile;
use rocket::http::Method;
use rocket_cors::{AllowedHeaders, AllowedOrigins, Cors, CorsOptions};
use std::io;
use std::path::{Path, PathBuf};
use local_ip_address::local_ip;
#[derive(Serialize, Deserialize, Debug)]
struct FlashData {
has_data: bool,
kind: Cow<'static, str>,
message: Cow<'static, str>,
pub fn make_cors() -> Cors {
let allowed_origins = AllowedOrigins::some_exact(&[
"http://localhost:3000",
"http://127.0.0.1:3000",
&format!("http://{}:8000",local_ip().unwrap())[..],
"http://localhost:8000",
"http://0.0.0.0:8000",
]);
CorsOptions {
allowed_origins,
allowed_methods: vec![Method::Get].into_iter().map(From::from).collect(), // 1.
allowed_headers: AllowedHeaders::some(&[
"Authorization",
"Accept",
"Access-Control-Allow-Origin",
]),
allow_credentials: true,
..Default::default()
}
.to_cors()
.expect("error while building CORS")
}
impl FlashData {
const EMPTY: Self = Self {
has_data: false,
message: Cow::Borrowed(""),
kind: Cow::Borrowed(""),
};
}
#[derive(Serialize, Deserialize, Debug)]
struct GameNightsData {
gamenights: Vec<schema::GameNight>,
flash: FlashData,
}
#[get("/gamenights")]
pub async fn gamenights(conn: schema::DbConn) -> Template {
let gamenights = schema::get_all_gamenights(conn).await;
let data = GameNightsData {
gamenights: gamenights,
flash: FlashData::EMPTY,
};
Template::render("gamenights", &data)
#[get("/<file..>", rank = 10)]
pub async fn files(file: PathBuf) -> Option<NamedFile> {
NamedFile::open(Path::new("../frontend/build/").join(file))
.await
.ok()
}
#[get("/")]
pub async fn index() -> Redirect {
Redirect::to(uri!(gamenights))
}
#[derive(Serialize, Deserialize, Debug)]
struct GameNightAddData {
post_url: String,
flash: FlashData,
}
#[get("/gamenight/add")]
pub async fn add_game_night(flash: Option<FlashMessage<'_>>) -> Template {
let flash_data = match flash {
None => FlashData::EMPTY,
Some(flash) => FlashData {
has_data: true,
message: Cow::Owned(flash.message().to_string()),
kind: Cow::Owned(flash.kind().to_string()),
},
};
let data = GameNightAddData {
post_url: "/api/gamenight".to_string(),
flash: flash_data,
};
Template::render("gamenight_add", &data)
}
#[derive(Serialize, Deserialize, Debug)]
struct RegisterData {
flash: FlashData,
}
#[get("/register")]
pub async fn register(flash: Option<FlashMessage<'_>>) -> Template {
let flash_data = match flash {
None => FlashData::EMPTY,
Some(flash) => FlashData {
has_data: true,
message: Cow::Owned(flash.message().to_string()),
kind: Cow::Owned(flash.kind().to_string()),
},
};
let data = RegisterData { flash: flash_data };
Template::render("register", &data)
pub async fn index() -> io::Result<NamedFile> {
NamedFile::open("../frontend/build/index.html").await
}

File diff suppressed because it is too large Load Diff

View File

@ -6,8 +6,10 @@
"@testing-library/jest-dom": "^5.16.4",
"@testing-library/react": "^13.0.1",
"@testing-library/user-event": "^13.5.0",
"react": "^18.0.0",
"react-dom": "^18.0.0",
"moment": "^2.29.3",
"react": "^18.1.0",
"react-datetime": "^3.1.1",
"react-dom": "^18.1.0",
"react-scripts": "5.0.1",
"web-vitals": "^2.1.4"
},
@ -15,7 +17,11 @@
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
"eject": "react-scripts eject",
"watch": "npm-watch"
},
"watch": {
"build": "src/"
},
"eslintConfig": {
"extends": [
@ -34,5 +40,9 @@
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"dotenv-cli": "^5.1.0",
"npm-watch": "^0.11.0"
}
}

View File

@ -24,7 +24,7 @@
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
<title>It's gamenight</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>

View File

@ -28,6 +28,19 @@
color: #61dafb;
}
fieldset label {
display: block;
text-align: left;
}
input {
display: block;
}
input[type=submit] {
margin:5px;
}
@keyframes App-logo-spin {
from {
transform: rotate(0deg);

View File

@ -1,25 +1,92 @@
import logo from './logo.svg';
import './App.css';
import React, { useState, useEffect } from 'react';
import MenuBar from './components/MenuBar';
import Login from './components/Login';
import Gamenights from './components/Gamenights'
import AddGameNight from './components/AddGameNight'
const localStorageUserKey = 'user';
function App() {
const [user, setUser] = useState(null);
const [gamenights, setGamenights] = useState([]);
const [flashData, setFlashData] = useState({});
const handleLogin = (input) => {
const requestOptions = {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(input)
};
fetch('api/login', requestOptions)
.then(response => response.json())
.then(data => {
if(data.result === "Ok") {
setUser(data.user);
localStorage.setItem(localStorageUserKey, JSON.stringify(data.user));
} else {
setFlashData({
type: "Error",
message: data.message
});
}
});
};
const onLogout = () => {
setUser(null);
localStorage.removeItem(localStorageUserKey);
};
const setFlash = (data) => {
setFlashData(data);
};
const refetchGamenights = () => {
setUser({...user});
};
useEffect(() => {
if (user !== null) {
const requestOptions = {
method: 'GET',
headers: { 'Authorization': `Bearer ${user.jwt}` },
};
fetch('api/gamenights', requestOptions)
.then(response => response.json())
.then(data => {
if(data.result === "Ok") {
setGamenights(data.gamenights)
} else {
setFlashData({
type: "Error",
message: data.message
});
}
});
}
}, [user])
useEffect(() => {
setUser(JSON.parse(localStorage.getItem(localStorageUserKey)));
}, []);
if(user === null) {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.js</code> and save to reload.
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
</header>
<Login onChange={handleLogin}/>
</div>
);
} else {
return (
<>
<MenuBar user={user} onLogout={onLogout} />
<AddGameNight user={user} setFlash={setFlash} refetchGamenights={refetchGamenights} />
<Gamenights user={user} setFlash={setFlash} refetchGamenights={refetchGamenights} gamenights={gamenights} />
</>
);
}
}
export default App;

View File

@ -0,0 +1,89 @@
import React, { useEffect, useState } from 'react';
import DateTime from 'react-datetime';
import "react-datetime/css/react-datetime.css";
function AddGameNight(props) {
const [expanded, setExpanded] = useState(false);
const [gameName, setGameName] = useState("");
const [date, setDate] = useState(Date.now());
const handleNameChange = (event) => {
setGameName(event.target.value);
};
const handleDateChange = (event) => {
setDate(event.target.value);
};
useEffect(() => {
if(!expanded) {
setGameName("");
setDate(null);
}
}, [expanded]);
const handleAddGamenight = (event) => {
event.preventDefault();
if (props.user !== null) {
let input = {
game: gameName,
datetime: date,
}
const requestOptions = {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${props.user.jwt}`
},
body: JSON.stringify(input)
};
fetch('api/gamenight', requestOptions)
.then(response => response.json())
.then(data => {
if(data.result !== "Ok") {
props.setFlash({
type: "Error",
message: data.message
});
} else {
setExpanded(false);
setGameName("");
setDate(null);
}
})
.then(() => props.refetchGamenights())
}
};
if(expanded) {
return (
<div className="Add-GameNight">
<form>
<fieldset>
<legend>Gamenight</legend>
<label for="gamename">Game name:</label>
<input id="gamename" name="gamename" type="text"
value={gameName}
onChange={handleNameChange} />
<label for="datetime">date:</label>
<DateTime id="datetime" onChange={(value) => { setDate(value) }} value={date} />
<button onClick={handleAddGamenight}>Submit</button>
</fieldset>
</form>
<button onClick={() => setExpanded(false)}>Discard</button>
</div>
);
} else {
return (
<button onClick={() => setExpanded(true)}>Expand</button>
);
}
}
export default AddGameNight

View File

@ -0,0 +1,54 @@
import React from 'react';
function Gamenights(props) {
const DeleteGameNight = (gameId) => {
if (props.user !== null) {
let input = {
game_id: gameId,
}
const requestOptions = {
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${props.user.jwt}`
},
body: JSON.stringify(input)
};
fetch('api/gamenight', requestOptions)
.then(response => response.json())
.then(data => {
if(data.result !== "Ok") {
props.setFlash({
type: "Error",
message: data.message
});
}
})
.then(() => props.refetchGamenights());
}
}
let gamenights = props.gamenights.map(g =>
(
<li>
<span>{g.game}</span>
{(props.user.id === g.owner_id || props.user.role === "Admin") &&
<button onClick={() =>DeleteGameNight(g.id, g.owner)}>
x
</button>
}
</li>
)
);
return (
<ul>
{gamenights}
</ul>
);
}
export default Gamenights

View File

@ -0,0 +1,42 @@
import React, { useState } from 'react';
function Login(props) {
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const handleUsernameChange = (event) => {
setUsername(event.target.value);
};
const handlePasswordChange = (event) => {
setPassword(event.target.value);
};
const handleLogin = (event) => {
props.onChange({ username: username, password: password });
event.preventDefault();
};
return (
<div className="Login-Component">
<form onSubmit={handleLogin}>
<fieldset>
<legend>Login</legend>
<label for="username">Username:</label>
<input id="username" name="username" type="text"
value={username}
onChange={handleUsernameChange} />
<label for="password">Password:</label>
<input id="password" name="password" type="password"
value={password}
onChange={handlePasswordChange} />
<input type="submit" value="Submit" />
</fieldset>
</form>
</div>
);
}
export default Login

View File

@ -0,0 +1,19 @@
import React from 'react';
function MenuBar(props) {
return (
<ul>
<li>
<a>Gamenight</a>
</li>
<li>
<a>User: {props.user.username}</a>
</li>
<li>
<button onClick={props.onLogout}>Logout</button>
</li>
</ul>
);
}
export default MenuBar;