mirror of
https://github.com/kristoferssolo/echoes-of-ascension.git
synced 2025-10-21 18:50:34 +00:00
refactor: use postgres
This commit is contained in:
parent
4d618905a9
commit
b26c36293c
140
Cargo.lock
generated
140
Cargo.lock
generated
@ -111,6 +111,7 @@ dependencies = [
|
|||||||
"tracing-bunyan-formatter",
|
"tracing-bunyan-formatter",
|
||||||
"tracing-log 0.2.0",
|
"tracing-log 0.2.0",
|
||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
|
"uuid",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -279,26 +280,6 @@ version = "1.6.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
|
checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "bindgen"
|
|
||||||
version = "0.69.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088"
|
|
||||||
dependencies = [
|
|
||||||
"bitflags",
|
|
||||||
"cexpr",
|
|
||||||
"clang-sys",
|
|
||||||
"itertools 0.12.1",
|
|
||||||
"lazy_static",
|
|
||||||
"lazycell",
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"regex",
|
|
||||||
"rustc-hash 1.1.0",
|
|
||||||
"shlex",
|
|
||||||
"syn",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "2.8.0"
|
version = "2.8.0"
|
||||||
@ -373,15 +354,6 @@ dependencies = [
|
|||||||
"shlex",
|
"shlex",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cexpr"
|
|
||||||
version = "0.6.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766"
|
|
||||||
dependencies = [
|
|
||||||
"nom",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg-if"
|
name = "cfg-if"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
@ -403,17 +375,6 @@ dependencies = [
|
|||||||
"windows-targets 0.52.6",
|
"windows-targets 0.52.6",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "clang-sys"
|
|
||||||
version = "1.8.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4"
|
|
||||||
dependencies = [
|
|
||||||
"glob",
|
|
||||||
"libc",
|
|
||||||
"libloading",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "codee"
|
name = "codee"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
@ -453,6 +414,18 @@ dependencies = [
|
|||||||
"toml",
|
"toml",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "config"
|
||||||
|
version = "0.15.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e329294a796e9b22329669c1f433a746983f9e324e07f4ef135be81bb2262de4"
|
||||||
|
dependencies = [
|
||||||
|
"pathdiff",
|
||||||
|
"serde",
|
||||||
|
"toml",
|
||||||
|
"winnow",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "const-oid"
|
name = "const-oid"
|
||||||
version = "0.9.6"
|
version = "0.9.6"
|
||||||
@ -901,12 +874,6 @@ version = "0.31.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
|
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "glob"
|
|
||||||
version = "0.3.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gloo-net"
|
name = "gloo-net"
|
||||||
version = "0.6.0"
|
version = "0.6.0"
|
||||||
@ -1337,15 +1304,6 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "itertools"
|
|
||||||
version = "0.12.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
|
|
||||||
dependencies = [
|
|
||||||
"either",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itertools"
|
name = "itertools"
|
||||||
version = "0.13.0"
|
version = "0.13.0"
|
||||||
@ -1389,12 +1347,6 @@ dependencies = [
|
|||||||
"spin",
|
"spin",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "lazycell"
|
|
||||||
version = "1.3.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "leptos"
|
name = "leptos"
|
||||||
version = "0.7.4"
|
version = "0.7.4"
|
||||||
@ -1418,7 +1370,7 @@ dependencies = [
|
|||||||
"paste",
|
"paste",
|
||||||
"rand",
|
"rand",
|
||||||
"reactive_graph",
|
"reactive_graph",
|
||||||
"rustc-hash 2.1.0",
|
"rustc-hash",
|
||||||
"send_wrapper",
|
"send_wrapper",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_qs",
|
"serde_qs",
|
||||||
@ -1464,7 +1416,7 @@ version = "0.7.4"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5d874993c7664d757677d056c8f46b5cb5365fe622005e1bf26050f4996e7e52"
|
checksum = "5d874993c7664d757677d056c8f46b5cb5365fe622005e1bf26050f4996e7e52"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"config",
|
"config 0.14.1",
|
||||||
"regex",
|
"regex",
|
||||||
"serde",
|
"serde",
|
||||||
"thiserror 2.0.11",
|
"thiserror 2.0.11",
|
||||||
@ -1530,7 +1482,7 @@ dependencies = [
|
|||||||
"cfg-if",
|
"cfg-if",
|
||||||
"convert_case",
|
"convert_case",
|
||||||
"html-escape",
|
"html-escape",
|
||||||
"itertools 0.13.0",
|
"itertools",
|
||||||
"leptos_hot_reload",
|
"leptos_hot_reload",
|
||||||
"prettyplease",
|
"prettyplease",
|
||||||
"proc-macro-error2",
|
"proc-macro-error2",
|
||||||
@ -1622,16 +1574,6 @@ version = "0.2.169"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
|
checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "libloading"
|
|
||||||
version = "0.8.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"windows-targets 0.48.5",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libm"
|
name = "libm"
|
||||||
version = "0.2.11"
|
version = "0.2.11"
|
||||||
@ -1644,7 +1586,6 @@ version = "0.30.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149"
|
checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bindgen",
|
|
||||||
"pkg-config",
|
"pkg-config",
|
||||||
"vcpkg",
|
"vcpkg",
|
||||||
]
|
]
|
||||||
@ -2191,7 +2132,7 @@ dependencies = [
|
|||||||
"hydration_context",
|
"hydration_context",
|
||||||
"or_poisoned",
|
"or_poisoned",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"rustc-hash 2.1.0",
|
"rustc-hash",
|
||||||
"send_wrapper",
|
"send_wrapper",
|
||||||
"serde",
|
"serde",
|
||||||
"slotmap",
|
"slotmap",
|
||||||
@ -2207,12 +2148,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "80bb1913eeb71f74028213455ee971550c2b3cb91b6acd5efa8a0f8dc59f5039"
|
checksum = "80bb1913eeb71f74028213455ee971550c2b3cb91b6acd5efa8a0f8dc59f5039"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"guardian",
|
"guardian",
|
||||||
"itertools 0.13.0",
|
"itertools",
|
||||||
"or_poisoned",
|
"or_poisoned",
|
||||||
"paste",
|
"paste",
|
||||||
"reactive_graph",
|
"reactive_graph",
|
||||||
"reactive_stores_macro",
|
"reactive_stores_macro",
|
||||||
"rustc-hash 2.1.0",
|
"rustc-hash",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -2322,12 +2263,6 @@ version = "0.1.24"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
|
checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rustc-hash"
|
|
||||||
version = "1.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc-hash"
|
name = "rustc-hash"
|
||||||
version = "2.1.0"
|
version = "2.1.0"
|
||||||
@ -2374,6 +2309,16 @@ version = "1.2.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "secrecy"
|
||||||
|
version = "0.10.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e891af845473308773346dc847b2c23ee78fe442e0472ac50e22a18a93d3ae5a"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "send_wrapper"
|
name = "send_wrapper"
|
||||||
version = "0.6.0"
|
version = "0.6.0"
|
||||||
@ -2392,6 +2337,17 @@ dependencies = [
|
|||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde-aux"
|
||||||
|
version = "4.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0d2e8bfba469d06512e11e3311d4d051a4a387a5b42d010404fecf3200321c95"
|
||||||
|
dependencies = [
|
||||||
|
"chrono",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.217"
|
version = "1.0.217"
|
||||||
@ -2463,13 +2419,20 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"app",
|
"app",
|
||||||
"axum",
|
"axum",
|
||||||
|
"config 0.15.6",
|
||||||
"leptos",
|
"leptos",
|
||||||
"leptos_axum",
|
"leptos_axum",
|
||||||
|
"secrecy",
|
||||||
|
"serde",
|
||||||
|
"serde-aux",
|
||||||
"sqlx",
|
"sqlx",
|
||||||
|
"thiserror 2.0.11",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tower",
|
"tower",
|
||||||
"tower-http",
|
"tower-http",
|
||||||
"tracing",
|
"tracing",
|
||||||
|
"tracing-log 0.2.0",
|
||||||
|
"uuid",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -2680,6 +2643,7 @@ dependencies = [
|
|||||||
"tokio-stream",
|
"tokio-stream",
|
||||||
"tracing",
|
"tracing",
|
||||||
"url",
|
"url",
|
||||||
|
"uuid",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -2761,6 +2725,7 @@ dependencies = [
|
|||||||
"stringprep",
|
"stringprep",
|
||||||
"thiserror 2.0.11",
|
"thiserror 2.0.11",
|
||||||
"tracing",
|
"tracing",
|
||||||
|
"uuid",
|
||||||
"whoami",
|
"whoami",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -2799,6 +2764,7 @@ dependencies = [
|
|||||||
"stringprep",
|
"stringprep",
|
||||||
"thiserror 2.0.11",
|
"thiserror 2.0.11",
|
||||||
"tracing",
|
"tracing",
|
||||||
|
"uuid",
|
||||||
"whoami",
|
"whoami",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -2824,6 +2790,7 @@ dependencies = [
|
|||||||
"sqlx-core",
|
"sqlx-core",
|
||||||
"tracing",
|
"tracing",
|
||||||
"url",
|
"url",
|
||||||
|
"uuid",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -2902,7 +2869,7 @@ dependencies = [
|
|||||||
"futures",
|
"futures",
|
||||||
"html-escape",
|
"html-escape",
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"itertools 0.13.0",
|
"itertools",
|
||||||
"js-sys",
|
"js-sys",
|
||||||
"linear-map",
|
"linear-map",
|
||||||
"next_tuple",
|
"next_tuple",
|
||||||
@ -2913,7 +2880,7 @@ dependencies = [
|
|||||||
"paste",
|
"paste",
|
||||||
"reactive_graph",
|
"reactive_graph",
|
||||||
"reactive_stores",
|
"reactive_stores",
|
||||||
"rustc-hash 2.1.0",
|
"rustc-hash",
|
||||||
"send_wrapper",
|
"send_wrapper",
|
||||||
"slotmap",
|
"slotmap",
|
||||||
"throw_error",
|
"throw_error",
|
||||||
@ -3397,6 +3364,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "b3758f5e68192bb96cc8f9b7e2c2cfdabb435499a28499a42f8f984092adad4b"
|
checksum = "b3758f5e68192bb96cc8f9b7e2c2cfdabb435499a28499a42f8f984092adad4b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom",
|
"getrandom",
|
||||||
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|||||||
@ -34,14 +34,18 @@ wasm-bindgen = "=0.2.100"
|
|||||||
sqlx = { version = "0.8", features = [
|
sqlx = { version = "0.8", features = [
|
||||||
"runtime-tokio",
|
"runtime-tokio",
|
||||||
"macros",
|
"macros",
|
||||||
"sqlite-unbundled",
|
"postgres",
|
||||||
|
"uuid",
|
||||||
"chrono",
|
"chrono",
|
||||||
"migrate",
|
"migrate",
|
||||||
] }
|
] }
|
||||||
|
uuid = { version = "1.12", features = ["v4", "serde"] }
|
||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
chrono = { version = "0.4", features = ["serde", "clock"] }
|
chrono = { version = "0.4", features = ["serde", "clock"] }
|
||||||
secrecy = { version = "0.10", features = ["serde"] }
|
secrecy = { version = "0.10", features = ["serde"] }
|
||||||
validator = "0.20"
|
validator = "0.20"
|
||||||
|
config = { version = "0.15", features = ["toml"], default-features = false }
|
||||||
|
serde-aux = "4"
|
||||||
|
|
||||||
# See https://github.com/leptos-rs/cargo-leptos for documentation of all the parameters.
|
# See https://github.com/leptos-rs/cargo-leptos for documentation of all the parameters.
|
||||||
|
|
||||||
|
|||||||
@ -20,8 +20,9 @@ tracing-subscriber.workspace = true
|
|||||||
tracing-bunyan-formatter.workspace = true
|
tracing-bunyan-formatter.workspace = true
|
||||||
tracing-log.workspace = true
|
tracing-log.workspace = true
|
||||||
sqlx.workspace = true
|
sqlx.workspace = true
|
||||||
serde.workspace = true
|
uuid.workspace = true
|
||||||
chrono.workspace = true
|
chrono.workspace = true
|
||||||
|
serde.workspace = true
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
|
|||||||
@ -1,9 +1,10 @@
|
|||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub struct User {
|
pub struct User {
|
||||||
pub id: i64,
|
pub id: Uuid,
|
||||||
pub username: String,
|
pub username: String,
|
||||||
pub code: String,
|
pub code: String,
|
||||||
pub created_at: DateTime<Utc>,
|
pub created_at: DateTime<Utc>,
|
||||||
|
|||||||
9
config/base.toml
Normal file
9
config/base.toml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
[application]
|
||||||
|
port = 8000
|
||||||
|
|
||||||
|
[database]
|
||||||
|
host = "127.0.0.1"
|
||||||
|
port = 5432
|
||||||
|
username = "postgres"
|
||||||
|
password = "password"
|
||||||
|
database_name = "maze_ascension"
|
||||||
5
config/local.toml
Normal file
5
config/local.toml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
[application]
|
||||||
|
host = "127.0.0.1"
|
||||||
|
|
||||||
|
[database]
|
||||||
|
require_ssl = false
|
||||||
5
config/production.toml
Normal file
5
config/production.toml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
[application]
|
||||||
|
host = "0.0.0.0"
|
||||||
|
|
||||||
|
[database]
|
||||||
|
require_ssl = true
|
||||||
10
justfile
10
justfile
@ -19,7 +19,7 @@ setup:
|
|||||||
# Development Commands
|
# Development Commands
|
||||||
|
|
||||||
# Start development server with hot reload
|
# Start development server with hot reload
|
||||||
dev: kill-server db-setup db-migrate
|
dev: kill-server db-migrate
|
||||||
cargo leptos watch | bunyan
|
cargo leptos watch | bunyan
|
||||||
|
|
||||||
# Run cargo check on both native and wasm targets
|
# Run cargo check on both native and wasm targets
|
||||||
@ -86,7 +86,7 @@ kill-server:
|
|||||||
|
|
||||||
# Setup the database
|
# Setup the database
|
||||||
db-setup:
|
db-setup:
|
||||||
sqlite3 ${DATABASE_URL#sqlite:} ".databases"
|
./scripts/init_db
|
||||||
|
|
||||||
alias migrate:=db-migrate
|
alias migrate:=db-migrate
|
||||||
alias m:=db-migrate
|
alias m:=db-migrate
|
||||||
@ -98,12 +98,6 @@ db-migrate:
|
|||||||
db-prepare:
|
db-prepare:
|
||||||
sqlx prepare
|
sqlx prepare
|
||||||
|
|
||||||
# Reset database
|
|
||||||
db-reset:
|
|
||||||
rm -f ${DATABASE_URL#sqlite:}
|
|
||||||
just db-setup
|
|
||||||
just db-migrate
|
|
||||||
|
|
||||||
alias migrations:=db-new-migration
|
alias migrations:=db-new-migration
|
||||||
# Create new migration
|
# Create new migration
|
||||||
db-new-migration name:
|
db-new-migration name:
|
||||||
|
|||||||
@ -1,20 +1,23 @@
|
|||||||
-- Add up migration script here
|
-- Add up migration script here
|
||||||
|
-- Enable UUID support
|
||||||
|
CREATE EXTENSION IF NOT EXISTS "pgcrypto";
|
||||||
|
|
||||||
-- Users table with login codes
|
-- Users table with login codes
|
||||||
CREATE TABLE IF NOT EXISTS users (
|
CREATE TABLE IF NOT EXISTS users (
|
||||||
id integer PRIMARY KEY AUTOINCREMENT,
|
id uuid PRIMARY KEY DEFAULT gen_random_uuid (),
|
||||||
username text NOT NULL UNIQUE,
|
username varchar(255) NOT NULL UNIQUE,
|
||||||
code text NOT NULL UNIQUE,
|
code varchar(255) NOT NULL UNIQUE,
|
||||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||||
);
|
);
|
||||||
|
|
||||||
-- Scores table with detailed game stats
|
-- Scores table with detailed game stats
|
||||||
CREATE TABLE IF NOT EXISTS scores (
|
CREATE TABLE IF NOT EXISTS scores (
|
||||||
id integer PRIMARY KEY AUTOINCREMENT,
|
id bigserial PRIMARY KEY,
|
||||||
user_id integer NOT NULL,
|
user_id uuid NOT NULL,
|
||||||
score integer NOT NULL,
|
score integer NOT NULL,
|
||||||
floor_reached integer NOT NULL,
|
floor_reached integer NOT NULL,
|
||||||
play_time_seconds integer NOT NULL,
|
play_time_seconds integer NOT NULL,
|
||||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
FOREIGN KEY (user_id) REFERENCES users (id)
|
FOREIGN KEY (user_id) REFERENCES users (id)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
46
scripts/init_db
Executable file
46
scripts/init_db
Executable file
@ -0,0 +1,46 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -eo pipefail
|
||||||
|
|
||||||
|
if ! [ -x "$(command -v psql)" ]; then
|
||||||
|
echo >&2 "Error: psql is not installed."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! [ -x "$(command -v sqlx)" ]; then
|
||||||
|
echo >&2 "Error: sqlx is not installed."
|
||||||
|
echo >&2 "Use:"
|
||||||
|
echo >&2 " cargo install sqlx-cli --no-default-features --features rustls,postgres"
|
||||||
|
echo >&2 "to install it."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
DB_USER="${POSTGRES_USER:=postgres}"
|
||||||
|
DB_PASSWORD="${POSTGRES_PASSWORD:=password}"
|
||||||
|
DB_NAME="${POSTGRES_DB:=maze_ascension}"
|
||||||
|
DB_PORT="${POSTGRES_PORT:=5432}"
|
||||||
|
DB_HOST="${POSTGRES_HOST:=localhost}"
|
||||||
|
|
||||||
|
if [[ -z "${SKIP_DOCKER}" ]]; then
|
||||||
|
docker run\
|
||||||
|
-e POSTGRES_USER=${DB_USER}\
|
||||||
|
-e POSTGRES_PASSWORD=${DB_PASSWORD}\
|
||||||
|
-e POSTGRES_DB=${DB_NAME}\
|
||||||
|
-p "${DB_PORT}":5432\
|
||||||
|
-d postgres\
|
||||||
|
postgres -N 1000
|
||||||
|
# Increase max number of connections for testing purposes
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Keep pinging Postgres until it's ready to accept commands
|
||||||
|
export PGPASSWORD="${DB_PASSWORD}"
|
||||||
|
until psql -h "${DB_HOST}" -U "${DB_USER}" -p "${DB_PORT}" -d "postgres" -c '\q'; do
|
||||||
|
>&2 echo "Postgres is still unavailable - sleeping"
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
|
||||||
|
>&2 echo "Postgres is still up and running on port ${DB_PORT} - runing migrations now!"
|
||||||
|
|
||||||
|
DATABASE_URL="postgres://${DB_USER}:${DB_PASSWORD}@${DB_HOST}:${DB_PORT}/${DB_NAME}"
|
||||||
|
export DATABASE_URL
|
||||||
|
sqlx database create
|
||||||
|
sqlx migrate run
|
||||||
@ -13,6 +13,13 @@ leptos_axum.workspace = true
|
|||||||
axum.workspace = true
|
axum.workspace = true
|
||||||
tokio.workspace = true
|
tokio.workspace = true
|
||||||
tower.workspace = true
|
tower.workspace = true
|
||||||
|
thiserror.workspace = true
|
||||||
tower-http.workspace = true
|
tower-http.workspace = true
|
||||||
tracing.workspace = true
|
tracing.workspace = true
|
||||||
|
tracing-log.workspace = true
|
||||||
sqlx.workspace = true
|
sqlx.workspace = true
|
||||||
|
serde.workspace = true
|
||||||
|
serde-aux.workspace = true
|
||||||
|
config.workspace = true
|
||||||
|
secrecy.workspace = true
|
||||||
|
uuid.workspace = true
|
||||||
|
|||||||
114
server/src/config.rs
Normal file
114
server/src/config.rs
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
use secrecy::{ExposeSecret, SecretString};
|
||||||
|
use serde::Deserialize;
|
||||||
|
use serde_aux::field_attributes::deserialize_number_from_string;
|
||||||
|
use sqlx::{
|
||||||
|
postgres::{PgConnectOptions, PgSslMode},
|
||||||
|
ConnectOptions,
|
||||||
|
};
|
||||||
|
use std::{fmt::Display, str::FromStr};
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
|
pub struct Settings {
|
||||||
|
pub database: DatabaseSettings,
|
||||||
|
pub application: ApplicationSettings,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
|
pub struct DatabaseSettings {
|
||||||
|
pub username: String,
|
||||||
|
pub password: SecretString,
|
||||||
|
#[serde(deserialize_with = "deserialize_number_from_string")]
|
||||||
|
pub port: u16,
|
||||||
|
pub host: String,
|
||||||
|
pub database_name: String,
|
||||||
|
pub require_ssl: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
|
pub struct ApplicationSettings {
|
||||||
|
#[serde(deserialize_with = "deserialize_number_from_string")]
|
||||||
|
pub port: u16,
|
||||||
|
pub host: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum Environment {
|
||||||
|
Local,
|
||||||
|
Production,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_config() -> Result<Settings, config::ConfigError> {
|
||||||
|
let base_path = std::env::current_dir().expect("Failed to determine current directory");
|
||||||
|
let config_directory = base_path.join("config");
|
||||||
|
let env: Environment = std::env::var("APP_ENVIRONMENT")
|
||||||
|
.unwrap_or_else(|_| "local".into())
|
||||||
|
.try_into()
|
||||||
|
.expect("Failed to parse APP_ENVIRONMENT");
|
||||||
|
|
||||||
|
let env_filename = format!("{}.toml", &env);
|
||||||
|
|
||||||
|
let settings = config::Config::builder()
|
||||||
|
.add_source(config::File::from(config_directory.join("base.toml")))
|
||||||
|
.add_source(config::File::from(config_directory.join(env_filename)))
|
||||||
|
.add_source(
|
||||||
|
config::Environment::with_prefix("APP")
|
||||||
|
.prefix_separator("_")
|
||||||
|
.separator("__"),
|
||||||
|
)
|
||||||
|
.build()?;
|
||||||
|
settings.try_deserialize::<Settings>()
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DatabaseSettings {
|
||||||
|
pub fn without_db(&self) -> PgConnectOptions {
|
||||||
|
let ssl_mode = if self.require_ssl {
|
||||||
|
PgSslMode::Require
|
||||||
|
} else {
|
||||||
|
PgSslMode::Prefer
|
||||||
|
};
|
||||||
|
|
||||||
|
PgConnectOptions::new()
|
||||||
|
.host(&self.host)
|
||||||
|
.username(&self.username)
|
||||||
|
.password(self.password.expose_secret())
|
||||||
|
.port(self.port)
|
||||||
|
.ssl_mode(ssl_mode)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_db(&self) -> PgConnectOptions {
|
||||||
|
self.without_db()
|
||||||
|
.database(&self.database_name)
|
||||||
|
.log_statements(tracing_log::log::LevelFilter::Trace)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Environment {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Environment::Local => write!(f, "local"),
|
||||||
|
Environment::Production => write!(f, "production"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<String> for Environment {
|
||||||
|
type Error = String;
|
||||||
|
fn try_from(value: String) -> Result<Self, Self::Error> {
|
||||||
|
match value.to_lowercase().as_str() {
|
||||||
|
"local" => Ok(Self::Local),
|
||||||
|
"production" => Ok(Self::Production),
|
||||||
|
other => Err(format!(
|
||||||
|
"{} is not supported environment. \
|
||||||
|
Use either `local` or `production`.",
|
||||||
|
other
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for Environment {
|
||||||
|
type Err = String;
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
s.to_owned().try_into()
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1 +1,2 @@
|
|||||||
mod scores;
|
mod user;
|
||||||
|
|
||||||
|
|||||||
13
server/src/db/user.rs
Normal file
13
server/src/db/user.rs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum UserError {
|
||||||
|
#[error("Database error: {0}")]
|
||||||
|
Database(#[from] sqlx::Error),
|
||||||
|
#[error("Username already taken")]
|
||||||
|
UsernameTaken,
|
||||||
|
#[error("User not found")]
|
||||||
|
NotFound,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn create_user() {}
|
||||||
@ -1 +1,4 @@
|
|||||||
|
pub mod config;
|
||||||
pub mod db;
|
pub mod db;
|
||||||
|
pub mod routes;
|
||||||
|
pub mod startup;
|
||||||
|
|||||||
@ -1,33 +1,17 @@
|
|||||||
use app::telemetry::{get_subscriber, init_subscriber};
|
use app::telemetry::{get_subscriber, init_subscriber};
|
||||||
use app::*;
|
|
||||||
use axum::Router;
|
|
||||||
use leptos::prelude::*;
|
use leptos::prelude::*;
|
||||||
use leptos_axum::{generate_route_list, LeptosRoutes};
|
use server::config::get_config;
|
||||||
use tracing::info;
|
use server::startup::{Application, ApplicationError};
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() -> Result<(), ApplicationError> {
|
||||||
let conf = get_configuration(None).unwrap();
|
|
||||||
let addr = conf.leptos_options.site_addr;
|
|
||||||
let leptos_options = conf.leptos_options;
|
|
||||||
// Generate the list of routes in your Leptos App
|
// Generate the list of routes in your Leptos App
|
||||||
let subscriber = get_subscriber("echoes-of-ascension-backend", "info", std::io::stdout);
|
let subscriber = get_subscriber("echoes-of-ascension-backend", "info", std::io::stdout);
|
||||||
init_subscriber(subscriber);
|
init_subscriber(subscriber);
|
||||||
|
|
||||||
let routes = generate_route_list(App);
|
let config = get_config().expect("Failed to read configuation.");
|
||||||
|
|
||||||
let app = Router::new()
|
let application = Application::build(config).await?;
|
||||||
.leptos_routes(&leptos_options, routes, {
|
application.run_until_stopped().await?;
|
||||||
let leptos_options = leptos_options.clone();
|
Ok(())
|
||||||
move || shell(leptos_options.clone())
|
|
||||||
})
|
|
||||||
.fallback(leptos_axum::file_and_error_handler(shell))
|
|
||||||
.with_state(leptos_options);
|
|
||||||
|
|
||||||
// run app with hyper
|
|
||||||
info!("listening on http://{}", &addr);
|
|
||||||
let listener = tokio::net::TcpListener::bind(&addr).await.unwrap();
|
|
||||||
axum::serve(listener, app.into_make_service())
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
|
|||||||
5
server/src/routes/health_check.rs
Normal file
5
server/src/routes/health_check.rs
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
use axum::{http::StatusCode, response::IntoResponse};
|
||||||
|
|
||||||
|
pub async fn health_check() -> impl IntoResponse {
|
||||||
|
StatusCode::OK
|
||||||
|
}
|
||||||
73
server/src/routes/mod.rs
Normal file
73
server/src/routes/mod.rs
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
mod health_check;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use app::{shell, App};
|
||||||
|
use axum::{
|
||||||
|
body::Bytes,
|
||||||
|
extract::MatchedPath,
|
||||||
|
http::{HeaderMap, Request},
|
||||||
|
response::Response,
|
||||||
|
routing::get,
|
||||||
|
Router,
|
||||||
|
};
|
||||||
|
use health_check::health_check;
|
||||||
|
|
||||||
|
use leptos_axum::{generate_route_list, LeptosRoutes};
|
||||||
|
use tower_http::{classify::ServerErrorsFailureClass, trace::TraceLayer};
|
||||||
|
use tracing::{info_span, Span};
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use crate::startup::AppState;
|
||||||
|
|
||||||
|
pub fn route(state: AppState) -> Router {
|
||||||
|
Router::new()
|
||||||
|
.merge(leptos_routes(state.clone()))
|
||||||
|
.merge(api_routes(state))
|
||||||
|
.layer(
|
||||||
|
TraceLayer::new_for_http()
|
||||||
|
.make_span_with(|request: &Request<_>| {
|
||||||
|
let matched_path = request
|
||||||
|
.extensions()
|
||||||
|
.get::<MatchedPath>()
|
||||||
|
.map(MatchedPath::as_str);
|
||||||
|
info_span!(
|
||||||
|
"http-request",
|
||||||
|
method = ?request.method(),
|
||||||
|
matched_path,
|
||||||
|
some_other_field = tracing::field::Empty,
|
||||||
|
request_id=%Uuid::new_v4(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.on_request(|_request: &Request<_>, _span: &Span| {})
|
||||||
|
.on_response(|_response: &Response<_>, _latency: Duration, _span: &Span| {})
|
||||||
|
.on_body_chunk(|_chunk: &Bytes, _latency: Duration, _span: &Span| {})
|
||||||
|
.on_eos(
|
||||||
|
|_trailers: Option<&HeaderMap>, _stream_duration: Duration, _span: &Span| {},
|
||||||
|
)
|
||||||
|
.on_failure(
|
||||||
|
|_error: ServerErrorsFailureClass, _latency: Duration, _span: &Span| {},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn leptos_routes(state: AppState) -> Router {
|
||||||
|
let leptos_options = state.leptos_options.clone();
|
||||||
|
let routes = generate_route_list(App);
|
||||||
|
|
||||||
|
Router::new()
|
||||||
|
.leptos_routes(&leptos_options, routes, {
|
||||||
|
let leptos_options = leptos_options.clone();
|
||||||
|
move || shell(leptos_options.clone())
|
||||||
|
})
|
||||||
|
.fallback(leptos_axum::file_and_error_handler(shell))
|
||||||
|
.with_state(leptos_options)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn api_routes(state: AppState) -> Router {
|
||||||
|
Router::new()
|
||||||
|
.nest(
|
||||||
|
"/api/v1",
|
||||||
|
Router::new().route("/health_check", get(health_check)),
|
||||||
|
)
|
||||||
|
.with_state(state)
|
||||||
|
}
|
||||||
75
server/src/startup.rs
Normal file
75
server/src/startup.rs
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use leptos::config::{errors::LeptosConfigError, get_configuration, LeptosOptions};
|
||||||
|
use sqlx::{postgres::PgPoolOptions, PgPool};
|
||||||
|
use thiserror::Error;
|
||||||
|
use tokio::{net::TcpListener, task::JoinHandle};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
config::{DatabaseSettings, Settings},
|
||||||
|
routes::route,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub type AppState = Arc<App>;
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum ApplicationError {
|
||||||
|
#[error("IO error: {0}")]
|
||||||
|
Io(#[from] std::io::Error),
|
||||||
|
|
||||||
|
#[error("Leptos configuration error: {0}")]
|
||||||
|
LeptosConfig(#[from] LeptosConfigError),
|
||||||
|
|
||||||
|
#[error("Database error: {0}")]
|
||||||
|
Database(#[from] sqlx::Error),
|
||||||
|
|
||||||
|
#[error("Server error: {0}")]
|
||||||
|
Server(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct App {
|
||||||
|
pub pool: PgPool,
|
||||||
|
pub leptos_options: LeptosOptions,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Application {
|
||||||
|
port: u16,
|
||||||
|
server: JoinHandle<Result<(), std::io::Error>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Application {
|
||||||
|
pub async fn build(config: Settings) -> Result<Self, ApplicationError> {
|
||||||
|
let pool = get_connection_pool(&config.database);
|
||||||
|
|
||||||
|
// Get Leptos configuration but override the address
|
||||||
|
let conf = get_configuration(None)?;
|
||||||
|
|
||||||
|
// Use application's address configuration
|
||||||
|
let addr = conf.leptos_options.site_addr;
|
||||||
|
let listener = TcpListener::bind(addr).await?;
|
||||||
|
let port = listener.local_addr()?.port();
|
||||||
|
|
||||||
|
let app_state = App {
|
||||||
|
pool,
|
||||||
|
leptos_options: conf.leptos_options,
|
||||||
|
}
|
||||||
|
.into();
|
||||||
|
let server = tokio::spawn(async move { axum::serve(listener, route(app_state)).await });
|
||||||
|
|
||||||
|
Ok(Self { port, server })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn port(&self) -> u16 {
|
||||||
|
self.port
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn run_until_stopped(self) -> Result<(), std::io::Error> {
|
||||||
|
self.server.await?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_connection_pool(config: &DatabaseSettings) -> PgPool {
|
||||||
|
PgPoolOptions::new().connect_lazy_with(config.with_db())
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user