diff --git a/backend/Cargo.lock b/backend/Cargo.lock index 934aa68..1b653ff 100644 --- a/backend/Cargo.lock +++ b/backend/Cargo.lock @@ -178,7 +178,10 @@ version = "0.1.0" dependencies = [ "async-openai", "axum", + "dashmap", "dotenv", + "governor", + "once_cell", "serde", "sqlx", "tokio", @@ -407,6 +410,20 @@ dependencies = [ "syn 2.0.106", ] +[[package]] +name = "dashmap" +version = "6.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" +dependencies = [ + "cfg-if", + "crossbeam-utils", + "hashbrown 0.14.5", + "lock_api", + "once_cell", + "parking_lot_core", +] + [[package]] name = "der" version = "0.7.10" @@ -560,6 +577,12 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + [[package]] name = "form_urlencoded" version = "1.2.2" @@ -718,6 +741,29 @@ version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" +[[package]] +name = "governor" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "444405bbb1a762387aa22dd569429533b54a1d8759d35d3b64cb39b0293eaa19" +dependencies = [ + "cfg-if", + "dashmap", + "futures-sink", + "futures-timer", + "futures-util", + "getrandom 0.3.3", + "hashbrown 0.15.5", + "nonzero_ext", + "parking_lot", + "portable-atomic", + "quanta", + "rand 0.9.2", + "smallvec", + "spinning_top", + "web-time", +] + [[package]] name = "hashbrown" version = "0.14.5" @@ -733,6 +779,11 @@ name = "hashbrown" version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", +] [[package]] name = "hashlink" @@ -1253,6 +1304,12 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "nonzero_ext" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38bf9645c8b145698bb0b18a4637dcacbc421ea49bef2317e4fd8065a387cf21" + [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -1420,6 +1477,12 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" +[[package]] +name = "portable-atomic" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" + [[package]] name = "potential_utf" version = "0.1.2" @@ -1447,6 +1510,21 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "quanta" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3ab5a9d756f0d97bdc89019bd2e4ea098cf9cde50ee7564dde6b81ccc8f06c7" +dependencies = [ + "crossbeam-utils", + "libc", + "once_cell", + "raw-cpuid", + "wasi 0.11.1+wasi-snapshot-preview1", + "web-sys", + "winapi", +] + [[package]] name = "quinn" version = "0.11.8" @@ -1576,6 +1654,15 @@ dependencies = [ "getrandom 0.3.3", ] +[[package]] +name = "raw-cpuid" +version = "11.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "498cd0dc59d73224351ee52a95fee0f1a617a2eae0e7d9d720cc622c73a54186" +dependencies = [ + "bitflags", +] + [[package]] name = "redox_syscall" version = "0.5.17" @@ -2044,6 +2131,15 @@ dependencies = [ "lock_api", ] +[[package]] +name = "spinning_top" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d96d2d1d716fb500937168cc09353ffdc7a012be8475ac7308e1bdf0e3923300" +dependencies = [ + "lock_api", +] + [[package]] name = "spki" version = "0.7.3" diff --git a/backend/Cargo.toml b/backend/Cargo.toml index 15d4577..04b6dd8 100644 --- a/backend/Cargo.toml +++ b/backend/Cargo.toml @@ -6,7 +6,10 @@ edition = "2024" [dependencies] async-openai = "0.29.2" axum = "0.8.4" +dashmap = "6.1.0" dotenv = "0.15.0" +governor = "0.10.1" +once_cell = "1.21.3" serde = "1.0.219" sqlx = { version = "0.7", features = ["runtime-tokio-rustls", "postgres", "chrono"] } tokio = { version = "1", features = ["full"] } diff --git a/backend/src/lib.rs b/backend/src/lib.rs index 56834a1..097f725 100644 --- a/backend/src/lib.rs +++ b/backend/src/lib.rs @@ -1,2 +1,3 @@ pub mod openai; pub mod database; +pub mod ratelimit; \ No newline at end of file diff --git a/backend/src/main.rs b/backend/src/main.rs index de12a51..8413f1c 100644 --- a/backend/src/main.rs +++ b/backend/src/main.rs @@ -3,6 +3,8 @@ use serde::{Deserialize, Serialize}; use std::net::SocketAddr; use backend::openai::{get_step_by_step_guidance, get_final_answer}; use backend::database::{is_valid_api_key, is_root_api_key, add_api_key as db_add_api_key, init_db, test_connection}; +use backend::ratelimit::check_rate_limit; + // An enum to determine the type of request #[derive(Debug, Deserialize)] pub enum OpenAiRequestType { @@ -71,6 +73,12 @@ async fn openai(Json(payload): Json) -> (StatusCode, Json { return (StatusCode::INTERNAL_SERVER_ERROR, Json(OpenAiResponse { diff --git a/backend/src/openai.rs b/backend/src/openai.rs index 13bb5aa..a1d9943 100644 --- a/backend/src/openai.rs +++ b/backend/src/openai.rs @@ -25,9 +25,16 @@ pub async fn get_step_by_step_guidance(image_b64: &str) -> Result Result>>> = + Lazy::new(DashMap::new); + +/// Checks if the given API key is within its rate limit. +/// Returns `true` if allowed, `false` if rate limit is exceeded. +pub fn check_rate_limit(api_key: &str) -> bool { + let limiter = API_KEY_LIMITERS + .entry(api_key.to_string()) + .or_insert_with(|| { + let quota = Quota::per_minute(NonZeroU32::new(RATE_LIMIT).unwrap()); + Arc::new(RateLimiter::direct_with_clock(quota, DefaultClock::default())) + }) + .clone(); + + limiter.check().is_ok() +} diff --git a/frontend/maths-online-app/src-tauri/tauri.conf.json b/frontend/maths-online-app/src-tauri/tauri.conf.json index e46eb6b..19af09e 100644 --- a/frontend/maths-online-app/src-tauri/tauri.conf.json +++ b/frontend/maths-online-app/src-tauri/tauri.conf.json @@ -1,6 +1,6 @@ { "$schema": "../node_modules/@tauri-apps/cli/config.schema.json", - "productName": "maths-online-app", + "productName": "Maths Online App", "version": "0.1.0", "identifier": "com.connorakey.dev", "build": {