add basic question uploading
This commit is contained in:
389
Cargo.lock
generated
389
Cargo.lock
generated
@@ -32,6 +32,48 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "askama"
|
||||||
|
version = "0.14.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f75363874b771be265f4ffe307ca705ef6f3baa19011c149da8674a87f1b75c4"
|
||||||
|
dependencies = [
|
||||||
|
"askama_derive",
|
||||||
|
"itoa",
|
||||||
|
"percent-encoding",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "askama_derive"
|
||||||
|
version = "0.14.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "129397200fe83088e8a68407a8e2b1f826cf0086b21ccdb866a722c8bcd3a94f"
|
||||||
|
dependencies = [
|
||||||
|
"askama_parser",
|
||||||
|
"basic-toml",
|
||||||
|
"memchr",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"rustc-hash",
|
||||||
|
"serde",
|
||||||
|
"serde_derive",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "askama_parser"
|
||||||
|
version = "0.14.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d6ab5630b3d5eaf232620167977f95eb51f3432fc76852328774afbd242d4358"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
"serde",
|
||||||
|
"serde_derive",
|
||||||
|
"winnow",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "atomic-waker"
|
name = "atomic-waker"
|
||||||
version = "1.1.2"
|
version = "1.1.2"
|
||||||
@@ -59,6 +101,15 @@ dependencies = [
|
|||||||
"windows-targets",
|
"windows-targets",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "basic-toml"
|
||||||
|
version = "0.1.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ba62675e8242a4c4e806d12f11d136e626e6c8361d6b829310732241652a178a"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "2.9.0"
|
version = "2.9.0"
|
||||||
@@ -79,9 +130,9 @@ checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.2.20"
|
version = "1.2.21"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "04da6a0d40b948dfc4fa8f5bbf402b0fc1a64a28dbf7d12ffd683550f2c1b63a"
|
checksum = "8691782945451c1c383942c4874dbe63814f61cb57ef773cda2972682b7bb3c0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"shlex",
|
"shlex",
|
||||||
]
|
]
|
||||||
@@ -112,6 +163,17 @@ version = "0.8.7"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
|
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "displaydoc"
|
||||||
|
version = "0.2.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "equivalent"
|
name = "equivalent"
|
||||||
version = "1.0.2"
|
version = "1.0.2"
|
||||||
@@ -124,6 +186,15 @@ version = "1.0.7"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "form_urlencoded"
|
||||||
|
version = "1.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
|
||||||
|
dependencies = [
|
||||||
|
"percent-encoding",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-channel"
|
name = "futures-channel"
|
||||||
version = "0.3.31"
|
version = "0.3.31"
|
||||||
@@ -190,9 +261,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "hashbrown"
|
||||||
version = "0.15.2"
|
version = "0.15.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
|
checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "http"
|
name = "http"
|
||||||
@@ -305,6 +376,145 @@ dependencies = [
|
|||||||
"cc",
|
"cc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "icu_collections"
|
||||||
|
version = "1.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526"
|
||||||
|
dependencies = [
|
||||||
|
"displaydoc",
|
||||||
|
"yoke",
|
||||||
|
"zerofrom",
|
||||||
|
"zerovec",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "icu_locid"
|
||||||
|
version = "1.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637"
|
||||||
|
dependencies = [
|
||||||
|
"displaydoc",
|
||||||
|
"litemap",
|
||||||
|
"tinystr",
|
||||||
|
"writeable",
|
||||||
|
"zerovec",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "icu_locid_transform"
|
||||||
|
version = "1.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e"
|
||||||
|
dependencies = [
|
||||||
|
"displaydoc",
|
||||||
|
"icu_locid",
|
||||||
|
"icu_locid_transform_data",
|
||||||
|
"icu_provider",
|
||||||
|
"tinystr",
|
||||||
|
"zerovec",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "icu_locid_transform_data"
|
||||||
|
version = "1.5.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7515e6d781098bf9f7205ab3fc7e9709d34554ae0b21ddbcb5febfa4bc7df11d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "icu_normalizer"
|
||||||
|
version = "1.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f"
|
||||||
|
dependencies = [
|
||||||
|
"displaydoc",
|
||||||
|
"icu_collections",
|
||||||
|
"icu_normalizer_data",
|
||||||
|
"icu_properties",
|
||||||
|
"icu_provider",
|
||||||
|
"smallvec",
|
||||||
|
"utf16_iter",
|
||||||
|
"utf8_iter",
|
||||||
|
"write16",
|
||||||
|
"zerovec",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "icu_normalizer_data"
|
||||||
|
version = "1.5.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c5e8338228bdc8ab83303f16b797e177953730f601a96c25d10cb3ab0daa0cb7"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "icu_properties"
|
||||||
|
version = "1.5.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5"
|
||||||
|
dependencies = [
|
||||||
|
"displaydoc",
|
||||||
|
"icu_collections",
|
||||||
|
"icu_locid_transform",
|
||||||
|
"icu_properties_data",
|
||||||
|
"icu_provider",
|
||||||
|
"tinystr",
|
||||||
|
"zerovec",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "icu_properties_data"
|
||||||
|
version = "1.5.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "85fb8799753b75aee8d2a21d7c14d9f38921b54b3dbda10f5a3c7a7b82dba5e2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "icu_provider"
|
||||||
|
version = "1.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9"
|
||||||
|
dependencies = [
|
||||||
|
"displaydoc",
|
||||||
|
"icu_locid",
|
||||||
|
"icu_provider_macros",
|
||||||
|
"stable_deref_trait",
|
||||||
|
"tinystr",
|
||||||
|
"writeable",
|
||||||
|
"yoke",
|
||||||
|
"zerofrom",
|
||||||
|
"zerovec",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "icu_provider_macros"
|
||||||
|
version = "1.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "idna"
|
||||||
|
version = "1.0.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e"
|
||||||
|
dependencies = [
|
||||||
|
"idna_adapter",
|
||||||
|
"smallvec",
|
||||||
|
"utf8_iter",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "idna_adapter"
|
||||||
|
version = "1.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71"
|
||||||
|
dependencies = [
|
||||||
|
"icu_normalizer",
|
||||||
|
"icu_properties",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "indexmap"
|
name = "indexmap"
|
||||||
version = "2.9.0"
|
version = "2.9.0"
|
||||||
@@ -337,6 +547,12 @@ version = "0.2.172"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
|
checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "litemap"
|
||||||
|
version = "0.7.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lock_api"
|
name = "lock_api"
|
||||||
version = "0.4.12"
|
version = "0.4.12"
|
||||||
@@ -426,6 +642,12 @@ dependencies = [
|
|||||||
"windows-targets",
|
"windows-targets",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "percent-encoding"
|
||||||
|
version = "2.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pin-project-lite"
|
name = "pin-project-lite"
|
||||||
version = "0.2.16"
|
version = "0.2.16"
|
||||||
@@ -471,12 +693,24 @@ 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 = "2.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustversion"
|
name = "rustversion"
|
||||||
version = "1.0.20"
|
version = "1.0.20"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2"
|
checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ryu"
|
||||||
|
version = "1.0.20"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "scopeguard"
|
name = "scopeguard"
|
||||||
version = "1.2.0"
|
version = "1.2.0"
|
||||||
@@ -507,6 +741,18 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_json"
|
||||||
|
version = "1.0.140"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373"
|
||||||
|
dependencies = [
|
||||||
|
"itoa",
|
||||||
|
"memchr",
|
||||||
|
"ryu",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_spanned"
|
name = "serde_spanned"
|
||||||
version = "0.6.8"
|
version = "0.6.8"
|
||||||
@@ -556,6 +802,12 @@ dependencies = [
|
|||||||
"windows-sys",
|
"windows-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "stable_deref_trait"
|
||||||
|
version = "1.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.101"
|
version = "2.0.101"
|
||||||
@@ -567,10 +819,22 @@ dependencies = [
|
|||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "synstructure"
|
||||||
|
version = "0.13.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "theseus-server"
|
name = "theseus-server"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"askama",
|
||||||
"chrono",
|
"chrono",
|
||||||
"http-body-util",
|
"http-body-util",
|
||||||
"hyper",
|
"hyper",
|
||||||
@@ -578,6 +842,17 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
"tokio",
|
"tokio",
|
||||||
"toml",
|
"toml",
|
||||||
|
"url",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tinystr"
|
||||||
|
version = "0.7.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f"
|
||||||
|
dependencies = [
|
||||||
|
"displaydoc",
|
||||||
|
"zerovec",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -700,6 +975,29 @@ 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 = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
|
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "url"
|
||||||
|
version = "2.5.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60"
|
||||||
|
dependencies = [
|
||||||
|
"form_urlencoded",
|
||||||
|
"idna",
|
||||||
|
"percent-encoding",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "utf16_iter"
|
||||||
|
version = "1.0.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "utf8_iter"
|
||||||
|
version = "1.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "want"
|
name = "want"
|
||||||
version = "0.3.1"
|
version = "0.3.1"
|
||||||
@@ -907,9 +1205,88 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winnow"
|
name = "winnow"
|
||||||
version = "0.7.7"
|
version = "0.7.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6cb8234a863ea0e8cd7284fcdd4f145233eb00fee02bbdd9861aec44e6477bc5"
|
checksum = "9e27d6ad3dac991091e4d35de9ba2d2d00647c5d0fc26c5496dee55984ae111b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "write16"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "writeable"
|
||||||
|
version = "0.5.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "yoke"
|
||||||
|
version = "0.7.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
"stable_deref_trait",
|
||||||
|
"yoke-derive",
|
||||||
|
"zerofrom",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "yoke-derive"
|
||||||
|
version = "0.7.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
"synstructure",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerofrom"
|
||||||
|
version = "0.1.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5"
|
||||||
|
dependencies = [
|
||||||
|
"zerofrom-derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerofrom-derive"
|
||||||
|
version = "0.1.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
"synstructure",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerovec"
|
||||||
|
version = "0.10.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079"
|
||||||
|
dependencies = [
|
||||||
|
"yoke",
|
||||||
|
"zerofrom",
|
||||||
|
"zerovec-derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerovec-derive"
|
||||||
|
version = "0.10.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
6
Makefile
6
Makefile
@@ -4,6 +4,9 @@ SRCS_RUST_THESEUS_SERVER := $(shell find theseus-server -type f)
|
|||||||
build: \
|
build: \
|
||||||
dst/release/theseus-server
|
dst/release/theseus-server
|
||||||
|
|
||||||
|
include config/make.mk
|
||||||
|
include client/make.mk
|
||||||
|
|
||||||
.PHONY: run
|
.PHONY: run
|
||||||
run: dst/dev.toml $(TARGETS_CLIENT)
|
run: dst/dev.toml $(TARGETS_CLIENT)
|
||||||
cargo run --package theseus-server -- dst/dev.toml
|
cargo run --package theseus-server -- dst/dev.toml
|
||||||
@@ -12,9 +15,6 @@ run: dst/dev.toml $(TARGETS_CLIENT)
|
|||||||
clean: client_clean
|
clean: client_clean
|
||||||
rm -rf dst
|
rm -rf dst
|
||||||
|
|
||||||
include config/make.mk
|
|
||||||
include client/make.mk
|
|
||||||
|
|
||||||
dst/release/theseus-server: $(SRCS_RUST_THESEUS_SERVER) $(TARGETS_CLIENT)
|
dst/release/theseus-server: $(SRCS_RUST_THESEUS_SERVER) $(TARGETS_CLIENT)
|
||||||
cargo build --package theseus-server --release
|
cargo build --package theseus-server --release
|
||||||
touch $@
|
touch $@
|
||||||
|
@@ -10,9 +10,14 @@
|
|||||||
<header>
|
<header>
|
||||||
<h1>Ask Stallman</h1>
|
<h1>Ask Stallman</h1>
|
||||||
</header>
|
</header>
|
||||||
|
{%- if message.len() != 0 -%}
|
||||||
|
<div class="notification {{ ntfy_class }}">
|
||||||
|
<p>{{ message }}</p>
|
||||||
|
</div>
|
||||||
|
{%- endif -%}
|
||||||
<div class="question">
|
<div class="question">
|
||||||
<form method="post">
|
<form method="post">
|
||||||
<input type="text" name="question" placeholder="Your question" autocomplete="off">
|
<input type="text" name="question" placeholder="Your question" autocomplete="off" maxlength="1024">
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div class="info">
|
<div class="info">
|
||||||
|
@@ -1,2 +1,12 @@
|
|||||||
[server]
|
[server]
|
||||||
bind_to = "[::1]:8080"
|
bind_to = "[::1]:8080"
|
||||||
|
max_question_body_size = 25
|
||||||
|
|
||||||
|
[performance]
|
||||||
|
memory_limit = 50
|
||||||
|
|
||||||
|
[maintenance]
|
||||||
|
interval = 60
|
||||||
|
|
||||||
|
[push]
|
||||||
|
endpoint = "[::1]:8081"
|
||||||
|
@@ -1,2 +1,3 @@
|
|||||||
dst/dev.toml: config/dev.toml
|
dst/dev.toml: config/dev.toml
|
||||||
|
@mkdir -p $(@D)
|
||||||
ln -f $< $@
|
ln -f $< $@
|
||||||
|
@@ -4,6 +4,7 @@ version = "0.1.0"
|
|||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
askama = "0.14.0"
|
||||||
chrono = "0.4.41"
|
chrono = "0.4.41"
|
||||||
http-body-util = "0.1.3"
|
http-body-util = "0.1.3"
|
||||||
hyper = { version = "1.6.0", features = ["full"] }
|
hyper = { version = "1.6.0", features = ["full"] }
|
||||||
@@ -11,3 +12,4 @@ hyper-util = { version = "0.1.11", features = ["full"] }
|
|||||||
serde = { version = "1.0.219", features = ["derive"] }
|
serde = { version = "1.0.219", features = ["derive"] }
|
||||||
tokio = { version = "1.44.2", features = ["full"] }
|
tokio = { version = "1.44.2", features = ["full"] }
|
||||||
toml = "0.8.22"
|
toml = "0.8.22"
|
||||||
|
url = "2.5.4"
|
||||||
|
2
theseus-server/askama.toml
Normal file
2
theseus-server/askama.toml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
[general]
|
||||||
|
dirs = ["../dst/"]
|
@@ -1,9 +1,8 @@
|
|||||||
use std::net::SocketAddr;
|
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
macro_rules! def_config {
|
macro_rules! def_config {
|
||||||
($(config $config_id: ident
|
($(config $config_id: ident
|
||||||
{ $([$namespace: ident $struct_name: ident]$($var_id: ident = $var_type: ty),*)*$(,)? })*
|
{ $([$namespace: ident $struct_name: ident]$($var_id: ident = $var_type: ty),*$(,)?)* })*
|
||||||
) => {
|
) => {
|
||||||
$(
|
$(
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
@@ -24,7 +23,17 @@ def_config! {
|
|||||||
config Config {
|
config Config {
|
||||||
|
|
||||||
[server Server]
|
[server Server]
|
||||||
bind_to = SocketAddr, // where to bind the server tcp socket in format: "<host>:<port>"
|
bind_to = std::net::SocketAddr, // recommended format: "<host>:<port>"
|
||||||
|
max_question_body_size = u64 // in bytes
|
||||||
|
|
||||||
|
[performance Performance]
|
||||||
|
memory_limit = usize,
|
||||||
|
|
||||||
|
[maintenance Maintenance]
|
||||||
|
interval = u64, // in seconds
|
||||||
|
|
||||||
|
[push Push]
|
||||||
|
endpoint = std::net::SocketAddr, // recommended format: "<host>:<port>"
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -17,4 +17,9 @@ macro_rules! log {
|
|||||||
let now = chrono::Local::now().to_rfc3339_opts(chrono::SecondsFormat::Secs, false);
|
let now = chrono::Local::now().to_rfc3339_opts(chrono::SecondsFormat::Secs, false);
|
||||||
println!(concat!("[{}] ERROR ", $text), now, $($($arg),*)?);
|
println!(concat!("[{}] ERROR ", $text), now, $($($arg),*)?);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
(fatal $text: literal$(, $($arg: expr),*$(,)?)?) => {
|
||||||
|
let now = chrono::Local::now().to_rfc3339_opts(chrono::SecondsFormat::Secs, false);
|
||||||
|
println!(concat!("[{}] !!FATAL ERROR!! ", $text), now, $($($arg),*)?);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
@@ -1,62 +1,259 @@
|
|||||||
use std::sync::{atomic::AtomicU32, Arc};
|
use std::{collections::HashSet, sync::{atomic::{AtomicUsize, Ordering}, Arc}};
|
||||||
use http_body_util::{combinators::BoxBody, BodyExt, Full};
|
use askama::Template;
|
||||||
|
use http_body_util::{combinators::BoxBody, BodyExt, Collected, Full};
|
||||||
use hyper::{
|
use hyper::{
|
||||||
body::{Bytes, Incoming},
|
body::{Body, Bytes, Incoming},
|
||||||
server::conn::http1, service::service_fn, Error, Method, Request, Response
|
header::HeaderValue, server::conn::http1, service::service_fn, Error, Method, Request, Response
|
||||||
};
|
};
|
||||||
use hyper_util::rt::TokioIo;
|
use hyper_util::rt::TokioIo;
|
||||||
use tokio::net::TcpListener;
|
use tokio::{net::TcpListener, sync::Mutex};
|
||||||
|
|
||||||
mod logger;
|
mod logger;
|
||||||
mod args;
|
mod args;
|
||||||
mod config;
|
mod config;
|
||||||
|
|
||||||
|
const CTYPE_FORM: HeaderValue = HeaderValue::from_static("application/x-www-form-urlencoded");
|
||||||
|
|
||||||
macro_rules! response {
|
macro_rules! response {
|
||||||
($status: ident $body: expr) => {{
|
(@server) => { "zumepro/ask_stallman" };
|
||||||
|
|
||||||
|
(file $req: expr, $source: literal, $mime: literal) => {
|
||||||
|
response!(
|
||||||
|
$req, OK include_str!(concat!("../../dst/", $source)),
|
||||||
|
CONTENT_TYPE: $mime,
|
||||||
|
CACHE_CONTROL: "max-age=180, public",
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
(main_page $req: expr, $message_class: literal, $message: literal) => {
|
||||||
|
response!( $req, OK MainPage {
|
||||||
|
ntfy_class: $message_class, message: $message, prefill_question: None
|
||||||
|
}.render()?, CONTENT_TYPE: "text/html")
|
||||||
|
};
|
||||||
|
|
||||||
|
(main_page $req: expr, $message_class: literal, $message: literal$(, $prefill: expr)?) => {
|
||||||
|
response!($req, OK MainPage {
|
||||||
|
ntfy_class: $message_class, message: $message, prefill_question: Some($prefill)
|
||||||
|
}.render()?, CONTENT_TYPE: "text/html")
|
||||||
|
};
|
||||||
|
|
||||||
|
($req: expr, $status: ident $body: expr) => {{
|
||||||
|
log!(info "{} \"{} {:?}\"", hyper::StatusCode::$status, $req.method(), $req.uri());
|
||||||
let mut res = Response::new(Full::new(Bytes::from($body)).map_err(|n| match n {}).boxed());
|
let mut res = Response::new(Full::new(Bytes::from($body)).map_err(|n| match n {}).boxed());
|
||||||
|
res.headers_mut().append(
|
||||||
|
hyper::header::SERVER, hyper::header::HeaderValue::from_static(response!(@server))
|
||||||
|
);
|
||||||
*res.status_mut() = hyper::StatusCode::$status;
|
*res.status_mut() = hyper::StatusCode::$status;
|
||||||
res
|
res
|
||||||
}};
|
}};
|
||||||
|
|
||||||
($status: ident $body: expr, $($hkey: ident : $hval: literal),*$(,)?) => {{
|
($req: expr, $status: ident $body: expr, $($hkey: ident : $hval: literal),*$(,)?) => {{
|
||||||
|
log!(info "{} \"{} {:?}\"", hyper::StatusCode::$status, $req.method(), $req.uri());
|
||||||
let mut res = Response::new(Full::new(Bytes::from($body)).map_err(|n| match n {}).boxed());
|
let mut res = Response::new(Full::new(Bytes::from($body)).map_err(|n| match n {}).boxed());
|
||||||
*res.status_mut() = hyper::StatusCode::$status;
|
*res.status_mut() = hyper::StatusCode::$status;
|
||||||
|
res.headers_mut().append(
|
||||||
|
hyper::header::SERVER, hyper::header::HeaderValue::from_static(response!(@server))
|
||||||
|
);
|
||||||
$(res.headers_mut()
|
$(res.headers_mut()
|
||||||
.append(hyper::header::$hkey, hyper::header::HeaderValue::from_static($hval));)*
|
.append(hyper::header::$hkey, hyper::header::HeaderValue::from_static($hval));)*
|
||||||
res
|
res
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(askama::Template, Default)]
|
||||||
|
#[template(path = "index.html")]
|
||||||
|
struct MainPage<'a> {
|
||||||
|
ntfy_class: &'a str,
|
||||||
|
message: &'a str,
|
||||||
|
prefill_question: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
enum RouterError {
|
||||||
|
Templating(askama::Error),
|
||||||
|
NotImplemented,
|
||||||
|
IO(Error),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for RouterError {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::Templating(e) => write!(f, "templating: {}", e),
|
||||||
|
Self::IO(e) => write!(f, "io: {}", e),
|
||||||
|
Self::NotImplemented => write!(f, "not implemented"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::error::Error for RouterError {
|
||||||
|
fn cause(&self) -> Option<&dyn std::error::Error> {
|
||||||
|
match self {
|
||||||
|
Self::Templating(e) => Some(e),
|
||||||
|
Self::IO(e) => Some(e),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||||
|
match self {
|
||||||
|
Self::Templating(e) => Some(e),
|
||||||
|
Self::IO(e) => Some(e),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn description(&self) -> &str {
|
||||||
|
match self {
|
||||||
|
Self::Templating(_) => "could not construct template",
|
||||||
|
Self::IO(_) => "i/o error",
|
||||||
|
Self::NotImplemented => "reached a code block not yet implemented",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Error> for RouterError {
|
||||||
|
fn from(value: Error) -> Self {
|
||||||
|
Self::IO(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<askama::Error> for RouterError {
|
||||||
|
fn from(value: askama::Error) -> Self {
|
||||||
|
Self::Templating(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async fn router(
|
async fn router(
|
||||||
req: Request<Incoming>,
|
req: Request<Incoming>,
|
||||||
_: Arc<SharedState>,
|
state: Arc<SharedState>,
|
||||||
) -> Result<Response<BoxBody<Bytes, Error>>, Error> {
|
) -> Result<Response<BoxBody<Bytes, Error>>, RouterError> {
|
||||||
match (req.method(), req.uri().path()) {
|
match (req.method(), req.uri().path()) {
|
||||||
(&Method::GET, "/") => Ok(response!(
|
// pages
|
||||||
OK include_str!("../../dst/index.html"),
|
(&Method::GET, "/") => Ok(response!(main_page req, "", "")),
|
||||||
CONTENT_TYPE: "text/html",
|
(&Method::POST, "/") => new_question(req, state).await,
|
||||||
)),
|
|
||||||
_ => Ok(response!(
|
// assets
|
||||||
|
(&Method::GET, "/script.js") => Ok(response!(file req, "script.js", "text/javascript")),
|
||||||
|
(&Method::GET, "/style.css") => Ok(response!(file req, "style.css", "text/css")),
|
||||||
|
|
||||||
|
_ => Ok(response!(req,
|
||||||
NOT_FOUND include_str!("../../dst/not_found.html"),
|
NOT_FOUND include_str!("../../dst/not_found.html"),
|
||||||
CONTENT_TYPE: "text/html",
|
CONTENT_TYPE: "text/html",
|
||||||
)),
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_config(path: &str) -> Result<config::Config, &'static str> {
|
fn parse_form<'a>(bytes: &'a [u8]) -> Result<std::borrow::Cow<'a, str>, ()> {
|
||||||
let Ok(file_contents) = std::fs::read_to_string(path) else {
|
let parsed = url::form_urlencoded::parse(bytes);
|
||||||
return Err("could not read the config file");
|
for field in parsed {
|
||||||
|
if field.0 == "question" {
|
||||||
|
return Ok(field.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn new_question(
|
||||||
|
mut req: Request<Incoming>,
|
||||||
|
state: Arc<SharedState>,
|
||||||
|
) -> Result<Response<BoxBody<Bytes, Error>>, RouterError> {
|
||||||
|
// check size
|
||||||
|
let body_size = req.body().size_hint().upper().unwrap_or(u64::MAX);
|
||||||
|
if body_size > state.config.server.max_question_body_size {
|
||||||
|
return Ok(response!(main_page req, "error", "Question was too long to add."));
|
||||||
|
}
|
||||||
|
|
||||||
|
// check headers
|
||||||
|
if req.headers().get(hyper::header::CONTENT_TYPE) != Some(&CTYPE_FORM) {
|
||||||
|
return Ok(response!(main_page req,
|
||||||
|
"error", "Your browser sent a POST request without form data. Please try again."
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// get question
|
||||||
|
let body = (&mut req).collect().await?.to_bytes();
|
||||||
|
let Ok(question) = parse_form(&body) else {
|
||||||
|
return Ok(response!(main_page req,
|
||||||
|
"error", "The question your browser sent was in invalid format. Please try again."
|
||||||
|
));
|
||||||
};
|
};
|
||||||
toml::from_str(&file_contents).map_err(|_| "invalid config file structure or fields")
|
let question = question.to_string();
|
||||||
|
|
||||||
|
// insert question
|
||||||
|
match state.questions.lock().await.add_new(question) {
|
||||||
|
Ok(()) => {},
|
||||||
|
Err(()) => return Ok(response!(main_page req,
|
||||||
|
"error", "We got too many questions in total. So we are not accepting new ones \
|
||||||
|
anymore. We are so sorry. :(")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(response!(main_page req, "info", "Your question was successfully added."))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn maintenance(state: Arc<SharedState>) {
|
||||||
|
let interval = std::time::Duration::from_secs(state.config.maintenance.interval);
|
||||||
|
log!(debug "started maintenance routine with {:?} interval", interval);
|
||||||
|
loop {
|
||||||
|
log!(debug "running maintenance");
|
||||||
|
tokio::time::sleep(interval).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_config(path: &str) -> Result<config::Config, String> {
|
||||||
|
let Ok(file_contents) = std::fs::read_to_string(path) else {
|
||||||
|
return Err("could not read the config file".to_string());
|
||||||
|
};
|
||||||
|
toml::from_str(&file_contents)
|
||||||
|
.map_err(|e| format!("invalid config file structure or fields: {:?}", e))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct Questions {
|
||||||
|
total_size: usize,
|
||||||
|
max_size: usize,
|
||||||
|
to_push: HashSet<String>,
|
||||||
|
pushed: HashSet<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Questions {
|
||||||
|
fn with_capacity(max_size: usize) -> Self {
|
||||||
|
Self {
|
||||||
|
max_size,
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_new(&mut self, question: String) -> Result<(), ()> {
|
||||||
|
if self.pushed.contains(&question) || self.to_push.contains(&question) { return Ok(()); }
|
||||||
|
if self.total_size + question.len() > self.max_size {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
self.total_size += question.len();
|
||||||
|
self.to_push.insert(question);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn consume_to_push(&mut self) -> HashSet<String> {
|
||||||
|
for question in self.to_push.iter() {
|
||||||
|
self.pushed.insert(question.clone());
|
||||||
|
}
|
||||||
|
std::mem::take(&mut self.to_push)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SharedState {
|
struct SharedState {
|
||||||
counter: AtomicU32,
|
config: config::Config,
|
||||||
|
questions: Mutex<Questions>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for SharedState {
|
impl SharedState {
|
||||||
fn default() -> Self {
|
fn new(config: config::Config) -> Self {
|
||||||
Self { counter: 0.into() }
|
let memory_limit = config.performance.memory_limit;
|
||||||
|
Self {
|
||||||
|
config,
|
||||||
|
questions: Mutex::new(Questions::with_capacity(memory_limit)),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,34 +270,39 @@ async fn main() {
|
|||||||
let config = match load_config(args.config_path()) {
|
let config = match load_config(args.config_path()) {
|
||||||
Ok(v) => v,
|
Ok(v) => v,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("{}", e);
|
log!(fatal "{}", e);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// shared state
|
// shared state
|
||||||
let state = Arc::new(SharedState::default());
|
let state = Arc::new(SharedState::new(config));
|
||||||
|
|
||||||
// server
|
// server initialization
|
||||||
let Ok(listener) = TcpListener::bind(config.server.bind_to).await else {
|
let Ok(listener) = TcpListener::bind(state.config.server.bind_to).await else {
|
||||||
eprintln!("unable to bind to: {:?}", config.server.bind_to);
|
log!(fatal "unable to bind to {:?}", state.config.server.bind_to);
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// server runtime
|
||||||
|
let state_maintenance = state.clone();
|
||||||
|
tokio::task::spawn(async move { maintenance(state_maintenance).await; });
|
||||||
|
log!(info "listening on {:?}", state.config.server.bind_to);
|
||||||
loop {
|
loop {
|
||||||
let Ok((stream, addr)) = listener.accept().await else {
|
let Ok((stream, addr)) = listener.accept().await else {
|
||||||
eprintln!("unable to accept new connections");
|
log!(fatal "unable to accept new connections");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let io = TokioIo::new(stream);
|
|
||||||
log!(debug "new connection from {:?}", addr);
|
log!(debug "new connection from {:?}", addr);
|
||||||
|
let io = TokioIo::new(stream);
|
||||||
let state_clone = state.clone();
|
let state_clone = state.clone();
|
||||||
tokio::task::spawn(async move {
|
tokio::task::spawn(async move {
|
||||||
if let Err(_) = http1::Builder::new().serve_connection(io, service_fn(move |req| {
|
if let Err(_) = http1::Builder::new().serve_connection(io, service_fn(move |req| {
|
||||||
router(req, state_clone.clone())
|
router(req, state_clone.clone())
|
||||||
})).await {
|
})).await {
|
||||||
println!("closed connection");
|
log!(debug "transport error to {:?}", addr);
|
||||||
}
|
}
|
||||||
|
log!(debug "closing connection to {:?}", addr);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user