summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock527
-rw-r--r--Cargo.toml2
-rw-r--r--src/database/database.rs9
-rw-r--r--src/database/sql/fetch_display_submissions.sql5
-rw-r--r--src/database/sql/initialize.sql14
-rw-r--r--src/database/sql/insert_submission.sql2
-rw-r--r--src/database/submission.rs7
-rw-r--r--src/main.rs10
-rw-r--r--src/routes/errors.rs4
-rw-r--r--src/routes/submission.rs16
-rw-r--r--src/routes/user.rs16
-rw-r--r--static/main.js2
-rw-r--r--static/register.html5
-rw-r--r--static/submission.js2
14 files changed, 601 insertions, 20 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 334c8bf..ae31ad3 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3,6 +3,28 @@
version = 4
[[package]]
+name = "aho-corasick"
+version = "1.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "ammonia"
+version = "4.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "17e913097e1a2124b46746c980134e8c954bc17a6a59bb3fde96f088d126dde6"
+dependencies = [
+ "cssparser",
+ "html5ever",
+ "maplit",
+ "tendril",
+ "url",
+]
+
+[[package]]
name = "anyhow"
version = "1.0.102"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -191,12 +213,14 @@ dependencies = [
name = "code_golf_server"
version = "0.1.0"
dependencies = [
+ "ammonia",
"argon2",
"axum",
"axum-extra",
"jsonwebtoken",
"r2d2",
"r2d2_sqlite",
+ "regex",
"serde",
"serde_json",
"time",
@@ -262,6 +286,29 @@ dependencies = [
]
[[package]]
+name = "cssparser"
+version = "0.35.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4e901edd733a1472f944a45116df3f846f54d37e67e68640ac8bb69689aca2aa"
+dependencies = [
+ "cssparser-macros",
+ "dtoa-short",
+ "itoa",
+ "phf",
+ "smallvec",
+]
+
+[[package]]
+name = "cssparser-macros"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331"
+dependencies = [
+ "quote",
+ "syn",
+]
+
+[[package]]
name = "curve25519-dalek"
version = "4.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -321,6 +368,32 @@ dependencies = [
]
[[package]]
+name = "displaydoc"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "dtoa"
+version = "1.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4c3cf4824e2d5f025c7b531afcb2325364084a16806f6d47fbc1f5fbd9960590"
+
+[[package]]
+name = "dtoa-short"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cd1511a7b6a56299bd043a9c167a6d2bfb37bf84a6dfceaba651168adfb43c87"
+dependencies = [
+ "dtoa",
+]
+
+[[package]]
name = "ecdsa"
version = "0.16.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -441,6 +514,16 @@ dependencies = [
]
[[package]]
+name = "futf"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df420e2e84819663797d1ec6544b13c5be84629e7bb00dc960d6917db2987843"
+dependencies = [
+ "mac",
+ "new_debug_unreachable",
+]
+
+[[package]]
name = "futures-channel"
version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -584,6 +667,17 @@ dependencies = [
]
[[package]]
+name = "html5ever"
+version = "0.35.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "55d958c2f74b664487a2035fe1dadb032c48718a03b63f3ab0b8537db8549ed4"
+dependencies = [
+ "log",
+ "markup5ever",
+ "match_token",
+]
+
+[[package]]
name = "http"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -670,12 +764,115 @@ dependencies = [
]
[[package]]
+name = "icu_collections"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c"
+dependencies = [
+ "displaydoc",
+ "potential_utf",
+ "utf8_iter",
+ "yoke",
+ "zerofrom",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_locale_core"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29"
+dependencies = [
+ "displaydoc",
+ "litemap",
+ "tinystr",
+ "writeable",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_normalizer"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4"
+dependencies = [
+ "icu_collections",
+ "icu_normalizer_data",
+ "icu_properties",
+ "icu_provider",
+ "smallvec",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_normalizer_data"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38"
+
+[[package]]
+name = "icu_properties"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de"
+dependencies = [
+ "icu_collections",
+ "icu_locale_core",
+ "icu_properties_data",
+ "icu_provider",
+ "zerotrie",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_properties_data"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14"
+
+[[package]]
+name = "icu_provider"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421"
+dependencies = [
+ "displaydoc",
+ "icu_locale_core",
+ "writeable",
+ "yoke",
+ "zerofrom",
+ "zerotrie",
+ "zerovec",
+]
+
+[[package]]
name = "id-arena"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954"
[[package]]
+name = "idna"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de"
+dependencies = [
+ "idna_adapter",
+ "smallvec",
+ "utf8_iter",
+]
+
+[[package]]
+name = "idna_adapter"
+version = "1.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714"
+dependencies = [
+ "icu_normalizer",
+ "icu_properties",
+]
+
+[[package]]
name = "indexmap"
version = "2.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -766,6 +963,12 @@ dependencies = [
]
[[package]]
+name = "litemap"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0"
+
+[[package]]
name = "lock_api"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -781,6 +984,40 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
[[package]]
+name = "mac"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4"
+
+[[package]]
+name = "maplit"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d"
+
+[[package]]
+name = "markup5ever"
+version = "0.35.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "311fe69c934650f8f19652b3946075f0fc41ad8757dbb68f1ca14e7900ecc1c3"
+dependencies = [
+ "log",
+ "tendril",
+ "web_atoms",
+]
+
+[[package]]
+name = "match_token"
+version = "0.35.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac84fd3f360fcc43dc5f5d186f02a94192761a080e8bc58621ad4d12296a58cf"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
name = "matchit"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -820,6 +1057,12 @@ dependencies = [
]
[[package]]
+name = "new_debug_unreachable"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086"
+
+[[package]]
name = "num-bigint"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -971,6 +1214,58 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
[[package]]
+name = "phf"
+version = "0.11.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078"
+dependencies = [
+ "phf_macros",
+ "phf_shared",
+]
+
+[[package]]
+name = "phf_codegen"
+version = "0.11.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a"
+dependencies = [
+ "phf_generator",
+ "phf_shared",
+]
+
+[[package]]
+name = "phf_generator"
+version = "0.11.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d"
+dependencies = [
+ "phf_shared",
+ "rand 0.8.6",
+]
+
+[[package]]
+name = "phf_macros"
+version = "0.11.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216"
+dependencies = [
+ "phf_generator",
+ "phf_shared",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "phf_shared"
+version = "0.11.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5"
+dependencies = [
+ "siphasher",
+]
+
+[[package]]
name = "pin-project-lite"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1004,6 +1299,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e"
[[package]]
+name = "potential_utf"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564"
+dependencies = [
+ "zerovec",
+]
+
+[[package]]
name = "powerfmt"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1019,6 +1323,12 @@ dependencies = [
]
[[package]]
+name = "precomputed-hash"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
+
+[[package]]
name = "prettyplease"
version = "0.2.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1140,6 +1450,35 @@ dependencies = [
]
[[package]]
+name = "regex"
+version = "1.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-automata",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.4.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.8.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a"
+
+[[package]]
name = "rfc6979"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1356,6 +1695,12 @@ dependencies = [
]
[[package]]
+name = "siphasher"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649"
+
+[[package]]
name = "slab"
version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1406,6 +1751,37 @@ dependencies = [
]
[[package]]
+name = "stable_deref_trait"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
+
+[[package]]
+name = "string_cache"
+version = "0.8.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bf776ba3fa74f83bf4b63c3dcbbf82173db2632ed8452cb2d891d33f459de70f"
+dependencies = [
+ "new_debug_unreachable",
+ "parking_lot",
+ "phf_shared",
+ "precomputed-hash",
+ "serde",
+]
+
+[[package]]
+name = "string_cache_codegen"
+version = "0.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c711928715f1fe0fe509c53b43e993a9a557babc2d0a3567d0a3006f1ac931a0"
+dependencies = [
+ "phf_generator",
+ "phf_shared",
+ "proc-macro2",
+ "quote",
+]
+
+[[package]]
name = "subtle"
version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1429,6 +1805,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263"
[[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]]
+name = "tendril"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d24a120c5fc464a3458240ee02c299ebcb9d67b5249c8848b09d639dca8d7bb0"
+dependencies = [
+ "futf",
+ "mac",
+ "utf-8",
+]
+
+[[package]]
name = "thiserror"
version = "2.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1480,6 +1878,16 @@ dependencies = [
]
[[package]]
+name = "tinystr"
+version = "0.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d"
+dependencies = [
+ "displaydoc",
+ "zerovec",
+]
+
+[[package]]
name = "tokio"
version = "1.52.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1616,6 +2024,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
[[package]]
+name = "url"
+version = "2.5.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed"
+dependencies = [
+ "form_urlencoded",
+ "idna",
+ "percent-encoding",
+ "serde",
+]
+
+[[package]]
+name = "utf-8"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
+
+[[package]]
+name = "utf8_iter"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
+
+[[package]]
name = "uuid"
version = "1.23.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1743,6 +2175,18 @@ dependencies = [
]
[[package]]
+name = "web_atoms"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "57ffde1dc01240bdf9992e3205668b235e59421fd085e8a317ed98da0178d414"
+dependencies = [
+ "phf",
+ "phf_codegen",
+ "string_cache",
+ "string_cache_codegen",
+]
+
+[[package]]
name = "windows-link"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1852,6 +2296,35 @@ dependencies = [
]
[[package]]
+name = "writeable"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4"
+
+[[package]]
+name = "yoke"
+version = "0.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "709fe23a0424b6a435d82152b1bd3fdfb0833487d5fa90d05d42762a9891fef5"
+dependencies = [
+ "stable_deref_trait",
+ "yoke-derive",
+ "zerofrom",
+]
+
+[[package]]
+name = "yoke-derive"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+ "synstructure",
+]
+
+[[package]]
name = "zerocopy"
version = "0.8.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1872,12 +2345,66 @@ dependencies = [
]
[[package]]
+name = "zerofrom"
+version = "0.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272"
+dependencies = [
+ "zerofrom-derive",
+]
+
+[[package]]
+name = "zerofrom-derive"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+ "synstructure",
+]
+
+[[package]]
name = "zeroize"
version = "1.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0"
[[package]]
+name = "zerotrie"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf"
+dependencies = [
+ "displaydoc",
+ "yoke",
+ "zerofrom",
+]
+
+[[package]]
+name = "zerovec"
+version = "0.11.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239"
+dependencies = [
+ "yoke",
+ "zerofrom",
+ "zerovec-derive",
+]
+
+[[package]]
+name = "zerovec-derive"
+version = "0.11.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
name = "zmij"
version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/Cargo.toml b/Cargo.toml
index 466085c..6fd96a7 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -4,12 +4,14 @@ version = "0.1.0"
edition = "2024"
[dependencies]
+ammonia = "4.1.2"
argon2 = { version = "0.5.3", features = ["std"] }
axum = "0.8.9"
axum-extra = { version = "0.12.6", features = ["cookie"] }
jsonwebtoken = { version = "10.3.0", features = ["rust_crypto"] }
r2d2 = "0.8.10"
r2d2_sqlite = "0.34.0"
+regex = "1.12.3"
serde = { version = "1.0.228", features = ["derive"] }
serde_json = "1.0.149"
time = "0.3.47"
diff --git a/src/database/database.rs b/src/database/database.rs
index 95c8bf6..ee9b6b9 100644
--- a/src/database/database.rs
+++ b/src/database/database.rs
@@ -173,6 +173,7 @@ impl Database {
language: &str,
details: &str,
code: &str,
+ code_length: i64,
) -> Result<Submission, DatabaseError> {
static QUERY: &str = include_str!("sql/insert_submission.sql");
let conn = self.pool
@@ -182,14 +183,15 @@ impl Database {
.map_err(|e| DatabaseError::Query(e.to_string()))?;
Ok(statement
- .query_one((user_id, problem_id, language, details, code), |row| {
+ .query_one((user_id, problem_id, language, details, code, code_length), |row| {
Ok(Submission::new(
row.get("id")?,
user_id,
problem_id,
language.to_owned(),
details.to_owned(),
- code.to_owned()
+ code.to_owned(),
+ code_length,
))
})
.map_err(|e| DatabaseError::Query(e.to_string()))?
@@ -213,6 +215,7 @@ impl Database {
row.get("language")?,
row.get("details")?,
row.get("code")?,
+ row.get("code_length")?,
))
})
.map_err(|e| DatabaseError::Query(e.to_string()))?
@@ -237,6 +240,7 @@ impl Database {
language: row.get("language")?,
details: row.get("details")?,
code: row.get("code")?,
+ code_length: row.get("code_length")?,
})
})
.map_err(|e| DatabaseError::Query(e.to_string()))?
@@ -261,6 +265,7 @@ impl Database {
language: row.get("language")?,
details: row.get("details")?,
code: row.get("code")?,
+ code_length: row.get("code_length")?,
})
})
.optional()
diff --git a/src/database/sql/fetch_display_submissions.sql b/src/database/sql/fetch_display_submissions.sql
index 5e836b3..aaceaca 100644
--- a/src/database/sql/fetch_display_submissions.sql
+++ b/src/database/sql/fetch_display_submissions.sql
@@ -3,8 +3,9 @@ submission.id AS id,
user.username AS username,
submission.language AS language,
submission.details AS details,
-submission.code AS code
+submission.code AS code,
+submission.code_length as code_length
FROM submission JOIN user
ON user.id = submission.user_id
WHERE submission.problem_id = ?1
-ORDER BY length(submission.code);
+ORDER BY submission.code_length;
diff --git a/src/database/sql/initialize.sql b/src/database/sql/initialize.sql
index 8c0f2ef..3569fd5 100644
--- a/src/database/sql/initialize.sql
+++ b/src/database/sql/initialize.sql
@@ -13,13 +13,13 @@ CREATE TABLE IF NOT EXISTS user (
);
CREATE TABLE IF NOT EXISTS submission (
- id INTEGER PRIMARY KEY,
- user_id INTEGER NOT NULL,
- problem_id INTEGER NOT NULL,
- language TEXT NOT NULL,
- details TEXT NOT NULL,
- code TEXT NOT NULL
-
+ id INTEGER PRIMARY KEY,
+ user_id INTEGER NOT NULL,
+ problem_id INTEGER NOT NULL,
+ language TEXT NOT NULL,
+ details TEXT NOT NULL,
+ code TEXT NOT NULL,
+ code_length INTEGER NOT NULL
);
INSERT INTO problem (title, description) VALUES (
diff --git a/src/database/sql/insert_submission.sql b/src/database/sql/insert_submission.sql
index 96df322..3f70bca 100644
--- a/src/database/sql/insert_submission.sql
+++ b/src/database/sql/insert_submission.sql
@@ -1 +1 @@
-INSERT INTO submission (user_id, problem_id, language, details, code) VALUES (?1, ?2, ?3, ?4, ?5) RETURNING id;
+INSERT INTO submission (user_id, problem_id, language, details, code, code_length) VALUES (?1, ?2, ?3, ?4, ?5, ?6) RETURNING id;
diff --git a/src/database/submission.rs b/src/database/submission.rs
index b117e17..0d6a131 100644
--- a/src/database/submission.rs
+++ b/src/database/submission.rs
@@ -8,6 +8,7 @@ pub struct Submission {
language: String,
details: String,
code: String,
+ code_length: i64,
}
impl Submission {
@@ -18,8 +19,9 @@ impl Submission {
language: String,
details: String,
code: String,
+ code_length: i64,
) -> Self {
- Self { id, user_id, problem_id, language, details, code }
+ Self { id, user_id, problem_id, language, details, code, code_length }
}
pub fn id(&self) -> i64 { self.id }
@@ -28,6 +30,7 @@ impl Submission {
pub fn language(&self) -> &str { &self.language }
pub fn details(&self) -> &str { &self.details }
pub fn code(&self) -> &str { &self.code }
+ pub fn code_length(&self) -> i64 { self.code_length }
}
#[derive(Serialize)]
@@ -37,4 +40,6 @@ pub struct DisplaySubmission {
pub(crate) language: String,
pub(crate) details: String,
pub(crate) code: String,
+ pub(crate) code_length: i64,
}
+
diff --git a/src/main.rs b/src/main.rs
index afa9821..b446402 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -14,11 +14,12 @@ use routes::user::{create_user, me};
use routes::auth::{login, logout};
use tower_http::services::ServeDir;
-use crate::{database::Database, routes::submission::{create_submission, get_display_submission, get_display_submissions, get_submissions_by_problem_id}, utils::register_admin};
+use crate::{database::Database, routes::submission::{create_submission, get_display_submission, get_display_submissions}, utils::register_admin};
#[derive(Clone)]
struct AppState {
secret: String,
+ register_code: String,
database: Database,
}
@@ -33,6 +34,12 @@ async fn main() {
eprintln!("missing environment variable JWT_SECRET");
return;
};
+
+ let Ok(register_code) = env::var("REGISTER_CODE") else {
+ eprintln!("missing environment variable REGISTER_CODE");
+ return;
+ };
+
let Ok(admin_email) = env::var("ADMIN_EMAIL") else {
eprintln!("missing environment variable ADMIN_EMAIL");
return;
@@ -53,6 +60,7 @@ async fn main() {
let state = AppState {
secret: secret,
+ register_code: register_code,
database: database,
};
diff --git a/src/routes/errors.rs b/src/routes/errors.rs
index f6a0901..5161e9a 100644
--- a/src/routes/errors.rs
+++ b/src/routes/errors.rs
@@ -3,6 +3,7 @@ use serde_json::{json};
pub enum RouteError {
Internal(String),
+ MalformedField(String),
UserCreateEmailExists(String),
UserCreateUsernameExists(String),
UnregisteredEmail(String),
@@ -30,6 +31,9 @@ impl IntoResponse for RouteError {
},
RouteError::NotFound(resource) => {
(StatusCode::NOT_FOUND, format!("{resource} not found"))
+ },
+ RouteError::MalformedField(field) => {
+ (StatusCode::BAD_REQUEST, format!("malformed {field}"))
}
};
diff --git a/src/routes/submission.rs b/src/routes/submission.rs
index b3cf2b9..99767c2 100644
--- a/src/routes/submission.rs
+++ b/src/routes/submission.rs
@@ -1,3 +1,4 @@
+use ammonia::clean_text;
use axum::{Json, extract::{Path, State}, http::StatusCode, response::IntoResponse};
use serde::Deserialize;
@@ -18,15 +19,22 @@ pub async fn create_submission(
) -> Result<impl IntoResponse, RouteError> {
let user_id = claims.sub;
+ let code_length = request.code.len() as i64;
+
+ let language = clean_text(&request.language);
+ let details = clean_text(&request.details);
+ let code = clean_text(&request.code);
+
match state.database.insert_submission(
user_id,
request.problem_id,
- &request.language,
- &request.details,
- &request.code
+ &language,
+ &details,
+ &code,
+ code_length,
) {
Ok(submission) => Ok((StatusCode::CREATED, Json(submission))),
- Err(_) => Err(RouteError::Internal(format!("unable to insert submission")))
+ Err(e) => Err(RouteError::Internal(format!("unable to insert submission {e:?}")))
}
}
diff --git a/src/routes/user.rs b/src/routes/user.rs
index 178a272..31a5824 100644
--- a/src/routes/user.rs
+++ b/src/routes/user.rs
@@ -1,6 +1,7 @@
use axum::extract::{Json, State};
use axum::http::StatusCode;
use axum::response::IntoResponse;
+use regex::Regex;
use serde::{Deserialize, Serialize};
use crate::AppState;
@@ -12,6 +13,7 @@ pub(crate) struct CreateUserRequest {
email: String,
username: String,
password: String,
+ register_code: String,
}
pub async fn create_user(
@@ -19,6 +21,20 @@ pub async fn create_user(
Json(request): Json<CreateUserRequest>
) -> Result<impl IntoResponse, RouteError> {
+ if request.register_code != state.register_code {
+ return Err(RouteError::AuthorizationFailure());
+ }
+
+ let email_re = Regex::new(r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$").unwrap();
+ if !email_re.is_match(&request.email) {
+ return Err(RouteError::MalformedField("email".into()));
+ }
+
+ let username_re = Regex::new(r"^[a-zA-Z0-9_\-]+$").unwrap();
+ if !username_re.is_match(&request.username) {
+ return Err(RouteError::MalformedField("username".into()));
+ }
+
match state.database.fetch_user_by_email(&request.email) {
Err(_) => return Err(RouteError::Internal("database action failed".into())),
Ok(Some(_)) => return Err(RouteError::UserCreateEmailExists(request.email)),
diff --git a/static/main.js b/static/main.js
index b3bf50f..1c2dbc1 100644
--- a/static/main.js
+++ b/static/main.js
@@ -83,7 +83,7 @@ function create_problem_element(problem, submissions) {
sub_row.appendChild(sub_lang);
const sub_size = document.createElement("td");
- sub_size.innerHTML = submission.code.length;
+ sub_size.innerHTML = `${submission.code_length} bytes`;
sub_row.appendChild(sub_size);
const sub_subm = document.createElement("td");
diff --git a/static/register.html b/static/register.html
index d7e183c..9f3b651 100644
--- a/static/register.html
+++ b/static/register.html
@@ -21,6 +21,8 @@
<span id="error-message" style="color: red"></span>
<br><br>
</div>
+ <p>Usernames should consist only of letters, numbers, underscores, and hyphens.</p>
+ <p>To help mitigate unauthorized access to my server, a registration code is required to create an account. Please see Daniel for the code.</p>
<label for="register-email">Email</label><br>
<input type="text" id="register-email" name="email">
<br><br>
@@ -30,6 +32,9 @@
<label for="register-password">Password</label><br>
<input type="password" id="register-password" name="password">
<br><br>
+ <label for="register-code">Registration Code</label><br>
+ <input type="password" id="register-code" name="register_code">
+ <br><br>
<input type="submit" value="Register">
</form>
diff --git a/static/submission.js b/static/submission.js
index bbe4075..4fbcf23 100644
--- a/static/submission.js
+++ b/static/submission.js
@@ -27,7 +27,7 @@ async function init() {
problem_div.appendChild(description);
document.getElementById("submission-author").innerHTML = `by ${submission.username}`;
- document.getElementById("submission-size").innerHTML = `${new Blob([submission.code]).size} bytes`;
+ document.getElementById("submission-size").innerHTML = `${submission.code_length} bytes`;
document.getElementById("submission-details").innerHTML = submission.details;
document.getElementById("submission-code").innerHTML = submission.code;