diff options
| -rw-r--r-- | Cargo.lock | 527 | ||||
| -rw-r--r-- | Cargo.toml | 2 | ||||
| -rw-r--r-- | src/database/database.rs | 9 | ||||
| -rw-r--r-- | src/database/sql/fetch_display_submissions.sql | 5 | ||||
| -rw-r--r-- | src/database/sql/initialize.sql | 14 | ||||
| -rw-r--r-- | src/database/sql/insert_submission.sql | 2 | ||||
| -rw-r--r-- | src/database/submission.rs | 7 | ||||
| -rw-r--r-- | src/main.rs | 10 | ||||
| -rw-r--r-- | src/routes/errors.rs | 4 | ||||
| -rw-r--r-- | src/routes/submission.rs | 16 | ||||
| -rw-r--r-- | src/routes/user.rs | 16 | ||||
| -rw-r--r-- | static/main.js | 2 | ||||
| -rw-r--r-- | static/register.html | 5 | ||||
| -rw-r--r-- | static/submission.js | 2 |
14 files changed, 601 insertions, 20 deletions
@@ -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" @@ -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; |
