add auth, FLCL reference
but I don't like sour drinks
This commit is contained in:
parent
44f1c1980c
commit
5d64a3137a
1
.env
1
.env
@ -1 +1,2 @@
|
|||||||
DATABASE_URL=sqlite:./sarmentine.db
|
DATABASE_URL=sqlite:./sarmentine.db
|
||||||
|
SESSION_SECRET=gzC0+m1ol9sPHaZpEU/AwIQVA9STkmxrWK6HhCI7ovlNdVzQqR0sg8VHQCRxURQFmVNEiQvJ8cx7G7nNd3b4jw
|
||||||
|
|||||||
421
Cargo.lock
generated
421
Cargo.lock
generated
@ -21,12 +21,33 @@ version = "0.2.21"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
|
checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "android_system_properties"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anyhow"
|
name = "anyhow"
|
||||||
version = "1.0.102"
|
version = "1.0.102"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
|
checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "argon2"
|
||||||
|
version = "0.5.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3c3610892ee6e0cbce8ae2700349fcf8f98adb0dbfbee85aec3c9179d29cc072"
|
||||||
|
dependencies = [
|
||||||
|
"base64ct",
|
||||||
|
"blake2",
|
||||||
|
"cpufeatures",
|
||||||
|
"password-hash",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "askama"
|
name = "askama"
|
||||||
version = "0.12.1"
|
version = "0.12.1"
|
||||||
@ -175,6 +196,12 @@ version = "0.21.7"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
|
checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "base64"
|
||||||
|
version = "0.22.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "base64ct"
|
name = "base64ct"
|
||||||
version = "1.8.3"
|
version = "1.8.3"
|
||||||
@ -199,6 +226,15 @@ dependencies = [
|
|||||||
"serde_core",
|
"serde_core",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "blake2"
|
||||||
|
version = "0.10.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe"
|
||||||
|
dependencies = [
|
||||||
|
"digest",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "block-buffer"
|
name = "block-buffer"
|
||||||
version = "0.10.4"
|
version = "0.10.4"
|
||||||
@ -208,6 +244,12 @@ dependencies = [
|
|||||||
"generic-array",
|
"generic-array",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bumpalo"
|
||||||
|
version = "3.20.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "byteorder"
|
name = "byteorder"
|
||||||
version = "1.5.0"
|
version = "1.5.0"
|
||||||
@ -236,12 +278,48 @@ version = "1.0.4"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "chrono"
|
||||||
|
version = "0.4.44"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0"
|
||||||
|
dependencies = [
|
||||||
|
"iana-time-zone",
|
||||||
|
"js-sys",
|
||||||
|
"num-traits",
|
||||||
|
"serde",
|
||||||
|
"wasm-bindgen",
|
||||||
|
"windows-link",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "const-oid"
|
name = "const-oid"
|
||||||
version = "0.9.6"
|
version = "0.9.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
|
checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cookie"
|
||||||
|
version = "0.18.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747"
|
||||||
|
dependencies = [
|
||||||
|
"base64 0.22.1",
|
||||||
|
"hmac",
|
||||||
|
"percent-encoding",
|
||||||
|
"rand",
|
||||||
|
"sha2",
|
||||||
|
"subtle",
|
||||||
|
"time",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "core-foundation-sys"
|
||||||
|
version = "0.8.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cpufeatures"
|
name = "cpufeatures"
|
||||||
version = "0.2.17"
|
version = "0.2.17"
|
||||||
@ -302,6 +380,16 @@ dependencies = [
|
|||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "deranged"
|
||||||
|
version = "0.5.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c"
|
||||||
|
dependencies = [
|
||||||
|
"powerfmt",
|
||||||
|
"serde_core",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "digest"
|
name = "digest"
|
||||||
version = "0.10.7"
|
version = "0.10.7"
|
||||||
@ -411,6 +499,20 @@ dependencies = [
|
|||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures"
|
||||||
|
version = "0.3.32"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d"
|
||||||
|
dependencies = [
|
||||||
|
"futures-channel",
|
||||||
|
"futures-core",
|
||||||
|
"futures-io",
|
||||||
|
"futures-sink",
|
||||||
|
"futures-task",
|
||||||
|
"futures-util",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-channel"
|
name = "futures-channel"
|
||||||
version = "0.3.32"
|
version = "0.3.32"
|
||||||
@ -455,6 +557,17 @@ version = "0.3.32"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718"
|
checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-macro"
|
||||||
|
version = "0.3.32"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.117",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-sink"
|
name = "futures-sink"
|
||||||
version = "0.3.32"
|
version = "0.3.32"
|
||||||
@ -475,6 +588,7 @@ checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-io",
|
"futures-io",
|
||||||
|
"futures-macro",
|
||||||
"futures-sink",
|
"futures-sink",
|
||||||
"futures-task",
|
"futures-task",
|
||||||
"memchr",
|
"memchr",
|
||||||
@ -705,6 +819,30 @@ dependencies = [
|
|||||||
"tower-service",
|
"tower-service",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "iana-time-zone"
|
||||||
|
version = "0.1.65"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470"
|
||||||
|
dependencies = [
|
||||||
|
"android_system_properties",
|
||||||
|
"core-foundation-sys",
|
||||||
|
"iana-time-zone-haiku",
|
||||||
|
"js-sys",
|
||||||
|
"log",
|
||||||
|
"wasm-bindgen",
|
||||||
|
"windows-core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[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]]
|
[[package]]
|
||||||
name = "icu_collections"
|
name = "icu_collections"
|
||||||
version = "2.2.0"
|
version = "2.2.0"
|
||||||
@ -832,6 +970,16 @@ version = "1.0.18"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
|
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "js-sys"
|
||||||
|
version = "0.3.94"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2e04e2ef80ce82e13552136fabeef8a5ed1f985a96805761cbb9a2c34e7664d9"
|
||||||
|
dependencies = [
|
||||||
|
"once_cell",
|
||||||
|
"wasm-bindgen",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lazy_static"
|
name = "lazy_static"
|
||||||
version = "1.5.0"
|
version = "1.5.0"
|
||||||
@ -901,6 +1049,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
|
checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"scopeguard",
|
"scopeguard",
|
||||||
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -990,6 +1139,12 @@ dependencies = [
|
|||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-conv"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c6673768db2d862beb9b39a78fdcb1a69439615d5794a1be50caa9bc92c81967"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-integer"
|
name = "num-integer"
|
||||||
version = "0.1.46"
|
version = "0.1.46"
|
||||||
@ -1049,6 +1204,17 @@ dependencies = [
|
|||||||
"windows-link",
|
"windows-link",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "password-hash"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166"
|
||||||
|
dependencies = [
|
||||||
|
"base64ct",
|
||||||
|
"rand_core",
|
||||||
|
"subtle",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "paste"
|
name = "paste"
|
||||||
version = "1.0.15"
|
version = "1.0.15"
|
||||||
@ -1118,6 +1284,12 @@ dependencies = [
|
|||||||
"zerovec",
|
"zerovec",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "powerfmt"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ppv-lite86"
|
name = "ppv-lite86"
|
||||||
version = "0.2.21"
|
version = "0.2.21"
|
||||||
@ -1215,6 +1387,25 @@ dependencies = [
|
|||||||
"bitflags",
|
"bitflags",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rmp"
|
||||||
|
version = "0.8.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4ba8be72d372b2c9b35542551678538b562e7cf86c3315773cae48dfbfe7790c"
|
||||||
|
dependencies = [
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rmp-serde"
|
||||||
|
version = "1.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "72f81bee8c8ef9b577d1681a70ebbc962c232461e397b22c208c43c04b67a155"
|
||||||
|
dependencies = [
|
||||||
|
"rmp",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rsa"
|
name = "rsa"
|
||||||
version = "0.9.10"
|
version = "0.9.10"
|
||||||
@ -1265,13 +1456,19 @@ name = "sarmentine"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
"argon2",
|
||||||
"askama",
|
"askama",
|
||||||
"askama_axum",
|
"askama_axum",
|
||||||
"axum",
|
"axum",
|
||||||
|
"chrono",
|
||||||
"dotenvy",
|
"dotenvy",
|
||||||
|
"rand_core",
|
||||||
|
"serde",
|
||||||
"sqlx",
|
"sqlx",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tower-http",
|
"tower-http",
|
||||||
|
"tower-sessions",
|
||||||
|
"tower-sessions-sqlx-store",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1474,6 +1671,7 @@ dependencies = [
|
|||||||
"atoi",
|
"atoi",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"bytes",
|
"bytes",
|
||||||
|
"chrono",
|
||||||
"crc",
|
"crc",
|
||||||
"crossbeam-queue",
|
"crossbeam-queue",
|
||||||
"either",
|
"either",
|
||||||
@ -1497,6 +1695,7 @@ dependencies = [
|
|||||||
"smallvec",
|
"smallvec",
|
||||||
"sqlformat",
|
"sqlformat",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
|
"time",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-stream",
|
"tokio-stream",
|
||||||
"tracing",
|
"tracing",
|
||||||
@ -1534,6 +1733,7 @@ dependencies = [
|
|||||||
"sha2",
|
"sha2",
|
||||||
"sqlx-core",
|
"sqlx-core",
|
||||||
"sqlx-mysql",
|
"sqlx-mysql",
|
||||||
|
"sqlx-postgres",
|
||||||
"sqlx-sqlite",
|
"sqlx-sqlite",
|
||||||
"syn 1.0.109",
|
"syn 1.0.109",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
@ -1548,10 +1748,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "1ed31390216d20e538e447a7a9b959e06ed9fc51c37b514b46eb758016ecd418"
|
checksum = "1ed31390216d20e538e447a7a9b959e06ed9fc51c37b514b46eb758016ecd418"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"atoi",
|
"atoi",
|
||||||
"base64",
|
"base64 0.21.7",
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"bytes",
|
"bytes",
|
||||||
|
"chrono",
|
||||||
"crc",
|
"crc",
|
||||||
"digest",
|
"digest",
|
||||||
"dotenvy",
|
"dotenvy",
|
||||||
@ -1579,6 +1780,7 @@ dependencies = [
|
|||||||
"sqlx-core",
|
"sqlx-core",
|
||||||
"stringprep",
|
"stringprep",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
|
"time",
|
||||||
"tracing",
|
"tracing",
|
||||||
"whoami",
|
"whoami",
|
||||||
]
|
]
|
||||||
@ -1590,9 +1792,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "7c824eb80b894f926f89a0b9da0c7f435d27cdd35b8c655b114e58223918577e"
|
checksum = "7c824eb80b894f926f89a0b9da0c7f435d27cdd35b8c655b114e58223918577e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"atoi",
|
"atoi",
|
||||||
"base64",
|
"base64 0.21.7",
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
|
"chrono",
|
||||||
"crc",
|
"crc",
|
||||||
"dotenvy",
|
"dotenvy",
|
||||||
"etcetera",
|
"etcetera",
|
||||||
@ -1617,6 +1820,7 @@ dependencies = [
|
|||||||
"sqlx-core",
|
"sqlx-core",
|
||||||
"stringprep",
|
"stringprep",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
|
"time",
|
||||||
"tracing",
|
"tracing",
|
||||||
"whoami",
|
"whoami",
|
||||||
]
|
]
|
||||||
@ -1628,6 +1832,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "b244ef0a8414da0bed4bb1910426e890b19e5e9bccc27ada6b797d05c55ae0aa"
|
checksum = "b244ef0a8414da0bed4bb1910426e890b19e5e9bccc27ada6b797d05c55ae0aa"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"atoi",
|
"atoi",
|
||||||
|
"chrono",
|
||||||
"flume",
|
"flume",
|
||||||
"futures-channel",
|
"futures-channel",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
@ -1639,6 +1844,7 @@ dependencies = [
|
|||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
"serde",
|
"serde",
|
||||||
"sqlx-core",
|
"sqlx-core",
|
||||||
|
"time",
|
||||||
"tracing",
|
"tracing",
|
||||||
"url",
|
"url",
|
||||||
"urlencoding",
|
"urlencoding",
|
||||||
@ -1739,6 +1945,37 @@ dependencies = [
|
|||||||
"syn 2.0.117",
|
"syn 2.0.117",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "time"
|
||||||
|
version = "0.3.47"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c"
|
||||||
|
dependencies = [
|
||||||
|
"deranged",
|
||||||
|
"itoa",
|
||||||
|
"num-conv",
|
||||||
|
"powerfmt",
|
||||||
|
"serde_core",
|
||||||
|
"time-core",
|
||||||
|
"time-macros",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "time-core"
|
||||||
|
version = "0.1.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "time-macros"
|
||||||
|
version = "0.2.27"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215"
|
||||||
|
dependencies = [
|
||||||
|
"num-conv",
|
||||||
|
"time-core",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tinystr"
|
name = "tinystr"
|
||||||
version = "0.8.3"
|
version = "0.8.3"
|
||||||
@ -1832,6 +2069,23 @@ dependencies = [
|
|||||||
"tracing",
|
"tracing",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tower-cookies"
|
||||||
|
version = "0.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4fd0118512cf0b3768f7fcccf0bef1ae41d68f2b45edc1e77432b36c97c56c6d"
|
||||||
|
dependencies = [
|
||||||
|
"async-trait",
|
||||||
|
"axum-core",
|
||||||
|
"cookie",
|
||||||
|
"futures-util",
|
||||||
|
"http",
|
||||||
|
"parking_lot",
|
||||||
|
"pin-project-lite",
|
||||||
|
"tower-layer",
|
||||||
|
"tower-service",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tower-http"
|
name = "tower-http"
|
||||||
version = "0.5.2"
|
version = "0.5.2"
|
||||||
@ -1869,6 +2123,71 @@ version = "0.3.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3"
|
checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tower-sessions"
|
||||||
|
version = "0.12.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "50571505955aaa8b73f2f40489953d92b4d7ff9eb9b2a8b4e11fee0dcdb2760e"
|
||||||
|
dependencies = [
|
||||||
|
"async-trait",
|
||||||
|
"http",
|
||||||
|
"time",
|
||||||
|
"tokio",
|
||||||
|
"tower-cookies",
|
||||||
|
"tower-layer",
|
||||||
|
"tower-service",
|
||||||
|
"tower-sessions-core",
|
||||||
|
"tower-sessions-memory-store",
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tower-sessions-core"
|
||||||
|
version = "0.12.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6293bf33f1977d5ef422c2e02f909eb2c3d7bf921d93557c40d4f1b130b84aa4"
|
||||||
|
dependencies = [
|
||||||
|
"async-trait",
|
||||||
|
"axum-core",
|
||||||
|
"base64 0.22.1",
|
||||||
|
"futures",
|
||||||
|
"http",
|
||||||
|
"parking_lot",
|
||||||
|
"rand",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"thiserror",
|
||||||
|
"time",
|
||||||
|
"tokio",
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tower-sessions-memory-store"
|
||||||
|
version = "0.12.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cec5f88eeef0f036e6900217034efbce733cbdf0528a85204eaaed90bc34c354"
|
||||||
|
dependencies = [
|
||||||
|
"async-trait",
|
||||||
|
"time",
|
||||||
|
"tokio",
|
||||||
|
"tower-sessions-core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tower-sessions-sqlx-store"
|
||||||
|
version = "0.12.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9442116d8ec67af57e2213f5b4007b6bb55d74c19eae429cd6525b7527844807"
|
||||||
|
dependencies = [
|
||||||
|
"async-trait",
|
||||||
|
"rmp-serde",
|
||||||
|
"sqlx",
|
||||||
|
"thiserror",
|
||||||
|
"time",
|
||||||
|
"tower-sessions-core",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tracing"
|
name = "tracing"
|
||||||
version = "0.1.44"
|
version = "0.1.44"
|
||||||
@ -2023,6 +2342,51 @@ version = "0.1.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b"
|
checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen"
|
||||||
|
version = "0.2.117"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0551fc1bb415591e3372d0bc4780db7e587d84e2a7e79da121051c5c4b89d0b0"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"once_cell",
|
||||||
|
"rustversion",
|
||||||
|
"wasm-bindgen-macro",
|
||||||
|
"wasm-bindgen-shared",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-macro"
|
||||||
|
version = "0.2.117"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7fbdf9a35adf44786aecd5ff89b4563a90325f9da0923236f6104e603c7e86be"
|
||||||
|
dependencies = [
|
||||||
|
"quote",
|
||||||
|
"wasm-bindgen-macro-support",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-macro-support"
|
||||||
|
version = "0.2.117"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dca9693ef2bab6d4e6707234500350d8dad079eb508dca05530c85dc3a529ff2"
|
||||||
|
dependencies = [
|
||||||
|
"bumpalo",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.117",
|
||||||
|
"wasm-bindgen-shared",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-shared"
|
||||||
|
version = "0.2.117"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "39129a682a6d2d841b6c429d0c51e5cb0ed1a03829d8b3d1e69a011e62cb3d3b"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-encoder"
|
name = "wasm-encoder"
|
||||||
version = "0.244.0"
|
version = "0.244.0"
|
||||||
@ -2067,12 +2431,65 @@ dependencies = [
|
|||||||
"wasite",
|
"wasite",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-core"
|
||||||
|
version = "0.62.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb"
|
||||||
|
dependencies = [
|
||||||
|
"windows-implement",
|
||||||
|
"windows-interface",
|
||||||
|
"windows-link",
|
||||||
|
"windows-result",
|
||||||
|
"windows-strings",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-implement"
|
||||||
|
version = "0.60.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.117",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-interface"
|
||||||
|
version = "0.59.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.117",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-link"
|
name = "windows-link"
|
||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-result"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5"
|
||||||
|
dependencies = [
|
||||||
|
"windows-link",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-strings"
|
||||||
|
version = "0.5.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091"
|
||||||
|
dependencies = [
|
||||||
|
"windows-link",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-sys"
|
name = "windows-sys"
|
||||||
version = "0.48.0"
|
version = "0.48.0"
|
||||||
|
|||||||
@ -9,7 +9,13 @@ tokio = { version = "1", features = ["full"] }
|
|||||||
askama = { version = "0.12", features = ["with-axum"] }
|
askama = { version = "0.12", features = ["with-axum"] }
|
||||||
askama_axum = "0.4"
|
askama_axum = "0.4"
|
||||||
tower-http = { version = "0.5", features = ["fs"] }
|
tower-http = { version = "0.5", features = ["fs"] }
|
||||||
sqlx = { version = "0.7", features = ["runtime-tokio", "sqlite", "migrate"] }
|
sqlx = { version = "0.7", features = ["runtime-tokio","chrono", "sqlite", "migrate"] }
|
||||||
dotenvy = "0.15"
|
dotenvy = "0.15"
|
||||||
|
argon2 = "0.5"
|
||||||
|
rand_core = { version = "0.6", features = ["std"] }
|
||||||
|
tower-sessions = { version = "0.12", features = ["signed"] }
|
||||||
|
tower-sessions-sqlx-store = { version = "0.12", features = ["sqlite"] }
|
||||||
|
serde = { version = "1", features = ["derive"] }
|
||||||
|
chrono = { version = "0.4", features = ["serde"] }
|
||||||
# Error handling
|
# Error handling
|
||||||
anyhow = "1"
|
anyhow = "1"
|
||||||
|
|||||||
172
src/handlers/auth.rs
Normal file
172
src/handlers/auth.rs
Normal file
@ -0,0 +1,172 @@
|
|||||||
|
use argon2::{
|
||||||
|
password_hash::{rand_core::OsRng, PasswordHash, PasswordHasher, PasswordVerifier, SaltString},
|
||||||
|
Argon2,
|
||||||
|
};
|
||||||
|
use askama::Template;
|
||||||
|
use axum::{
|
||||||
|
extract::{Form, State},
|
||||||
|
response::{Html, IntoResponse, Redirect, Response},
|
||||||
|
};
|
||||||
|
use serde::Deserialize;
|
||||||
|
use sqlx::SqlitePool;
|
||||||
|
use tower_sessions::Session;
|
||||||
|
|
||||||
|
use crate::models::user::User;
|
||||||
|
|
||||||
|
const SESSION_USER_ID_KEY: &str = "user_id";
|
||||||
|
|
||||||
|
// ~~ Templates
|
||||||
|
|
||||||
|
#[derive(Template)]
|
||||||
|
#[template(path = "auth/register.html")]
|
||||||
|
pub struct RegisterTemplate {
|
||||||
|
pub title: String,
|
||||||
|
pub current_user: Option<crate::CurrentUser>,
|
||||||
|
pub error: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Template)]
|
||||||
|
#[template(path = "auth/login.html")]
|
||||||
|
pub struct LoginTemplate {
|
||||||
|
pub title: String,
|
||||||
|
pub current_user: Option<crate::CurrentUser>,
|
||||||
|
pub error: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render<T: Template>(t: T) -> Response {
|
||||||
|
match t.render() {
|
||||||
|
Ok(html) => Html(html).into_response(),
|
||||||
|
Err(_) => (axum::http::StatusCode::INTERNAL_SERVER_ERROR, "render error").into_response(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ~~ Form types
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct RegisterForm {
|
||||||
|
pub username: String,
|
||||||
|
pub email: String,
|
||||||
|
pub password: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct LoginForm {
|
||||||
|
pub username: String,
|
||||||
|
pub password: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
// ~~ Handlers
|
||||||
|
|
||||||
|
pub async fn register_page() -> Response {
|
||||||
|
render(RegisterTemplate {
|
||||||
|
title: "register".into(),
|
||||||
|
current_user: None,
|
||||||
|
error: None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn register_submit(
|
||||||
|
State(pool): State<SqlitePool>,
|
||||||
|
session: Session,
|
||||||
|
Form(form): Form<RegisterForm>,
|
||||||
|
) -> Response {
|
||||||
|
macro_rules! register_err {
|
||||||
|
($msg:expr) => {
|
||||||
|
return render(RegisterTemplate {
|
||||||
|
title: "register".into(),
|
||||||
|
current_user: None,
|
||||||
|
error: Some($msg.into()),
|
||||||
|
})
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if form.username.len() < 2 || form.username.len() > 32 {
|
||||||
|
register_err!("username must be between 2 and 32 characters");
|
||||||
|
}
|
||||||
|
if form.password.len() < 8 {
|
||||||
|
register_err!("password must be at least 8 characters");
|
||||||
|
}
|
||||||
|
|
||||||
|
let existing_user: Option<User> = User::find_by_username(&pool, &form.username)
|
||||||
|
.await
|
||||||
|
.unwrap_or(None);
|
||||||
|
if existing_user.is_some() {
|
||||||
|
register_err!("that username is already taken");
|
||||||
|
}
|
||||||
|
|
||||||
|
let existing_email: Option<User> = User::find_by_email(&pool, &form.email)
|
||||||
|
.await
|
||||||
|
.unwrap_or(None);
|
||||||
|
if existing_email.is_some() {
|
||||||
|
register_err!("an account with that email already exists");
|
||||||
|
}
|
||||||
|
|
||||||
|
let salt = SaltString::generate(&mut OsRng);
|
||||||
|
let hash = Argon2::default()
|
||||||
|
.hash_password(form.password.as_bytes(), &salt)
|
||||||
|
.expect("password hashing failed")
|
||||||
|
.to_string();
|
||||||
|
|
||||||
|
let user_id: i64 = User::insert(&pool, &form.username, &form.email, &hash)
|
||||||
|
.await
|
||||||
|
.expect("failed to insert user");
|
||||||
|
|
||||||
|
session.insert(SESSION_USER_ID_KEY, user_id).await.unwrap();
|
||||||
|
|
||||||
|
Redirect::to("/").into_response()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn login_page() -> Response {
|
||||||
|
render(LoginTemplate {
|
||||||
|
title: "login".into(),
|
||||||
|
current_user: None,
|
||||||
|
error: None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn login_submit(
|
||||||
|
State(pool): State<SqlitePool>,
|
||||||
|
session: Session,
|
||||||
|
Form(form): Form<LoginForm>,
|
||||||
|
) -> Response {
|
||||||
|
let fail = || {
|
||||||
|
render(LoginTemplate {
|
||||||
|
title: "login".into(),
|
||||||
|
current_user: None,
|
||||||
|
error: Some("incorrect username or password".into()),
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
let user: Option<User> = User::find_by_username(&pool, &form.username)
|
||||||
|
.await
|
||||||
|
.unwrap_or(None);
|
||||||
|
|
||||||
|
let user = match user {
|
||||||
|
Some(u) => u,
|
||||||
|
None => return fail(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let parsed_hash = PasswordHash::new(&user.password).expect("invalid stored hash");
|
||||||
|
if Argon2::default()
|
||||||
|
.verify_password(form.password.as_bytes(), &parsed_hash)
|
||||||
|
.is_err()
|
||||||
|
{
|
||||||
|
return fail();
|
||||||
|
}
|
||||||
|
|
||||||
|
session.insert(SESSION_USER_ID_KEY, user.id).await.unwrap();
|
||||||
|
|
||||||
|
Redirect::to("/").into_response()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn logout(session: Session) -> Response {
|
||||||
|
session.flush().await.unwrap();
|
||||||
|
Redirect::to("/auth/login").into_response()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ~~ Session helper
|
||||||
|
|
||||||
|
pub async fn get_current_user(session: &Session, pool: &SqlitePool) -> Option<User> {
|
||||||
|
let user_id: i64 = session.get(SESSION_USER_ID_KEY).await.ok()??;
|
||||||
|
User::find_by_id(pool, user_id).await.unwrap_or(None)
|
||||||
|
}
|
||||||
1
src/handlers/mod.rs
Normal file
1
src/handlers/mod.rs
Normal file
@ -0,0 +1 @@
|
|||||||
|
pub mod auth;
|
||||||
52
src/main.rs
52
src/main.rs
@ -1,10 +1,22 @@
|
|||||||
mod db;
|
mod db;
|
||||||
|
mod handlers;
|
||||||
|
mod models;
|
||||||
|
|
||||||
use askama::Template;
|
use askama::Template;
|
||||||
use askama_axum::IntoResponse;
|
use axum::{
|
||||||
use axum::{routing::get, Router};
|
extract::State,
|
||||||
|
response::{Html, IntoResponse, Response},
|
||||||
|
routing::{get, post},
|
||||||
|
Router,
|
||||||
|
};
|
||||||
use sqlx::SqlitePool;
|
use sqlx::SqlitePool;
|
||||||
use tower_http::services::ServeDir;
|
use tower_http::services::ServeDir;
|
||||||
|
use tower_sessions::{cookie::SameSite, SessionManagerLayer};
|
||||||
|
use tower_sessions_sqlx_store::SqliteStore;
|
||||||
|
|
||||||
|
use handlers::auth::{
|
||||||
|
get_current_user, login_page, login_submit, logout, register_page, register_submit,
|
||||||
|
};
|
||||||
|
|
||||||
pub struct CurrentUser {
|
pub struct CurrentUser {
|
||||||
pub username: String,
|
pub username: String,
|
||||||
@ -17,10 +29,21 @@ struct IndexTemplate {
|
|||||||
current_user: Option<CurrentUser>,
|
current_user: Option<CurrentUser>,
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn index() -> impl IntoResponse {
|
async fn index(
|
||||||
IndexTemplate {
|
State(pool): State<SqlitePool>,
|
||||||
|
session: tower_sessions::Session,
|
||||||
|
) -> Response {
|
||||||
|
let user: Option<models::user::User> = get_current_user(&session, &pool).await;
|
||||||
|
let current_user = user.map(|u| CurrentUser { username: u.username });
|
||||||
|
|
||||||
|
let tmpl = IndexTemplate {
|
||||||
title: "home".into(),
|
title: "home".into(),
|
||||||
current_user: None,
|
current_user,
|
||||||
|
};
|
||||||
|
|
||||||
|
match tmpl.render() {
|
||||||
|
Ok(html) => Html(html).into_response(),
|
||||||
|
Err(_) => (axum::http::StatusCode::INTERNAL_SERVER_ERROR, "render error").into_response(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -33,9 +56,28 @@ async fn main() {
|
|||||||
|
|
||||||
let pool: SqlitePool = db::connect(&database_url).await;
|
let pool: SqlitePool = db::connect(&database_url).await;
|
||||||
|
|
||||||
|
let session_store = SqliteStore::new(pool.clone());
|
||||||
|
session_store.migrate().await.expect("session store migration failed");
|
||||||
|
|
||||||
|
let session_secret = std::env::var("SESSION_SECRET")
|
||||||
|
.unwrap_or_else(|_| "change-me-in-production-use-a-long-random-string!!".into());
|
||||||
|
|
||||||
|
let session_layer = SessionManagerLayer::new(session_store)
|
||||||
|
.with_secure(false)
|
||||||
|
.with_same_site(SameSite::Lax)
|
||||||
|
.with_signed(tower_sessions::cookie::Key::from(
|
||||||
|
session_secret.as_bytes(),
|
||||||
|
));
|
||||||
|
|
||||||
let app = Router::new()
|
let app = Router::new()
|
||||||
.route("/", get(index))
|
.route("/", get(index))
|
||||||
|
.route("/auth/register", get(register_page))
|
||||||
|
.route("/auth/register", post(register_submit))
|
||||||
|
.route("/auth/login", get(login_page))
|
||||||
|
.route("/auth/login", post(login_submit))
|
||||||
|
.route("/auth/logout", post(logout))
|
||||||
.nest_service("/static", ServeDir::new("static"))
|
.nest_service("/static", ServeDir::new("static"))
|
||||||
|
.layer(session_layer)
|
||||||
.with_state(pool);
|
.with_state(pool);
|
||||||
|
|
||||||
let listener = tokio::net::TcpListener::bind("127.0.0.1:3000")
|
let listener = tokio::net::TcpListener::bind("127.0.0.1:3000")
|
||||||
|
|||||||
1
src/models/mod.rs
Normal file
1
src/models/mod.rs
Normal file
@ -0,0 +1 @@
|
|||||||
|
pub mod user;
|
||||||
70
src/models/user.rs
Normal file
70
src/models/user.rs
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
use chrono::NaiveDateTime;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use sqlx::{FromRow, SqlitePool};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, FromRow)]
|
||||||
|
pub struct User {
|
||||||
|
pub id: i64,
|
||||||
|
pub username: String,
|
||||||
|
pub email: String,
|
||||||
|
#[serde(skip_serializing)]
|
||||||
|
pub password: String,
|
||||||
|
pub bio: Option<String>,
|
||||||
|
pub avatar_url: Option<String>,
|
||||||
|
pub role: String,
|
||||||
|
pub created_at: NaiveDateTime,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl User {
|
||||||
|
pub async fn find_by_id(pool: &SqlitePool, id: i64) -> sqlx::Result<Option<User>> {
|
||||||
|
sqlx::query_as(
|
||||||
|
"SELECT id, username, email, password, bio, avatar_url, role, created_at
|
||||||
|
FROM users WHERE id = ?"
|
||||||
|
)
|
||||||
|
.bind(id)
|
||||||
|
.fetch_optional(pool)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn find_by_username(pool: &SqlitePool, username: &str) -> sqlx::Result<Option<User>> {
|
||||||
|
sqlx::query_as(
|
||||||
|
"SELECT id, username, email, password, bio, avatar_url, role, created_at
|
||||||
|
FROM users WHERE username = ?"
|
||||||
|
)
|
||||||
|
.bind(username)
|
||||||
|
.fetch_optional(pool)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn find_by_email(pool: &SqlitePool, email: &str) -> sqlx::Result<Option<User>> {
|
||||||
|
sqlx::query_as(
|
||||||
|
"SELECT id, username, email, password, bio, avatar_url, role, created_at
|
||||||
|
FROM users WHERE email = ?"
|
||||||
|
)
|
||||||
|
.bind(email)
|
||||||
|
.fetch_optional(pool)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn insert(
|
||||||
|
pool: &SqlitePool,
|
||||||
|
username: &str,
|
||||||
|
email: &str,
|
||||||
|
password_hash: &str,
|
||||||
|
) -> sqlx::Result<i64> {
|
||||||
|
let result = sqlx::query(
|
||||||
|
"INSERT INTO users (username, email, password) VALUES (?, ?, ?)"
|
||||||
|
)
|
||||||
|
.bind(username)
|
||||||
|
.bind(email)
|
||||||
|
.bind(password_hash)
|
||||||
|
.execute(pool)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(result.last_insert_rowid())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_admin(&self) -> bool {
|
||||||
|
self.role == "admin"
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -254,7 +254,7 @@ pre {
|
|||||||
height: calc(31px * 2);
|
height: calc(31px * 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* nav right (login/logout) */
|
/* ── nav right (login/logout) ───────────────────────────── */
|
||||||
.nav {
|
.nav {
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
}
|
}
|
||||||
@ -285,7 +285,7 @@ pre {
|
|||||||
background: #d4879c;
|
background: #d4879c;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* layout: no sidebar */
|
/* ── layout: no sidebar ─────────────────────────────────── */
|
||||||
.layout {
|
.layout {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
@ -293,3 +293,48 @@ pre {
|
|||||||
.main {
|
.main {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ── forms ──────────────────────────────────────────────── */
|
||||||
|
.form-field {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-label {
|
||||||
|
display: block;
|
||||||
|
font-family: 'Silkscreen', monospace;
|
||||||
|
font-size: 10px;
|
||||||
|
color: #b48ead;
|
||||||
|
text-transform: uppercase;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-input {
|
||||||
|
width: 100%;
|
||||||
|
background: #fdf6e3;
|
||||||
|
border: 2px solid #b48ead;
|
||||||
|
color: #4a4060;
|
||||||
|
font-family: 'VT323', monospace;
|
||||||
|
font-size: 18px;
|
||||||
|
padding: 4px 8px;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-input:focus {
|
||||||
|
border-color: #d4879c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-submit {
|
||||||
|
background: #4a4060;
|
||||||
|
color: #fdf6e3;
|
||||||
|
border: 2px solid #4a4060;
|
||||||
|
font-family: 'Silkscreen', monospace;
|
||||||
|
font-size: 11px;
|
||||||
|
padding: 6px 14px;
|
||||||
|
cursor: pointer;
|
||||||
|
margin-top: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-submit:hover {
|
||||||
|
background: #d4879c;
|
||||||
|
border-color: #d4879c;
|
||||||
|
}
|
||||||
|
|||||||
31
templates/auth/login.html
Normal file
31
templates/auth/login.html
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="site-title">login</div>
|
||||||
|
<div class="site-subtitle">:: i'm a computer ::</div>
|
||||||
|
|
||||||
|
<hr class="divider">
|
||||||
|
|
||||||
|
{% if let Some(err) = error %}
|
||||||
|
<div class="post" style="border-color:#d4879c; margin-bottom:12px;">
|
||||||
|
<div class="post-body" style="color:#d4879c;">⚠ {{ err }}</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<div class="post">
|
||||||
|
<form method="post" action="/auth/login">
|
||||||
|
<div class="form-field">
|
||||||
|
<label class="form-label">username</label>
|
||||||
|
<input class="form-input" type="text" name="username" required autocomplete="username">
|
||||||
|
</div>
|
||||||
|
<div class="form-field">
|
||||||
|
<label class="form-label">password</label>
|
||||||
|
<input class="form-input" type="password" name="password" required autocomplete="current-password">
|
||||||
|
</div>
|
||||||
|
<button class="form-submit" type="submit">login →</button>
|
||||||
|
</form>
|
||||||
|
<div class="post-meta" style="margin-top:10px;">
|
||||||
|
new here? <a href="/auth/register" style="color:#d4879c;">register here</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
35
templates/auth/register.html
Normal file
35
templates/auth/register.html
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="site-title">register</div>
|
||||||
|
<div class="site-subtitle">:: no more free gabagool ::</div>
|
||||||
|
|
||||||
|
<hr class="divider">
|
||||||
|
|
||||||
|
{% if let Some(err) = error %}
|
||||||
|
<div class="post" style="border-color:#d4879c; margin-bottom:12px;">
|
||||||
|
<div class="post-body" style="color:#d4879c;">⚠ {{ err }}</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<div class="post">
|
||||||
|
<form method="post" action="/auth/register">
|
||||||
|
<div class="form-field">
|
||||||
|
<label class="form-label">username</label>
|
||||||
|
<input class="form-input" type="text" name="username" required minlength="2" maxlength="32" autocomplete="username">
|
||||||
|
</div>
|
||||||
|
<div class="form-field">
|
||||||
|
<label class="form-label">email</label>
|
||||||
|
<input class="form-input" type="email" name="email" required autocomplete="email">
|
||||||
|
</div>
|
||||||
|
<div class="form-field">
|
||||||
|
<label class="form-label">password</label>
|
||||||
|
<input class="form-input" type="password" name="password" required minlength="8" autocomplete="new-password">
|
||||||
|
</div>
|
||||||
|
<button class="form-submit" type="submit">register →</button>
|
||||||
|
</form>
|
||||||
|
<div class="post-meta" style="margin-top:10px;">
|
||||||
|
already have an account? <a href="/auth/login" style="color:#d4879c;">login here</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
@ -35,7 +35,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="status-bar">
|
<div class="status-bar">
|
||||||
<span>rhizomatic queer ascendance</span>
|
<span>but i don't like sour drinks</span>
|
||||||
<span>toasterdragon.com</span>
|
<span>toasterdragon.com</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user