From 9ed04aacae59d7cc737b68ced0954f2823032580 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 16 Feb 2026 18:50:13 +0000 Subject: [PATCH 1/3] Initial plan From 53c8a058db4466eb3ddd1d6acb2f683662de53ae Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 16 Feb 2026 18:57:08 +0000 Subject: [PATCH 2/3] Fix route ordering for embed-key endpoint to prevent 403 errors Co-authored-by: Yashb404 <139128977+Yashb404@users.noreply.github.com> --- server/src/handlers/project.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/src/handlers/project.rs b/server/src/handlers/project.rs index 3f39912..fbdcb51 100644 --- a/server/src/handlers/project.rs +++ b/server/src/handlers/project.rs @@ -147,13 +147,13 @@ fn validate_csrf_protection(headers: &HeaderMap) -> Result<(), (StatusCode, Stri pub fn routes() -> Router { Router::new() .route("/api/my-projects", get(list_user_projects)) - .route("/api/project/:username/:slug", get(get_project)) - .route("/api/project/:slug", delete(delete_project)) .route("/api/project/:slug/embed-key", get(get_embed_key)) .route( "/api/project/:slug/whitelist", get(get_whitelist).post(add_to_whitelist).delete(remove_from_whitelist), ) + .route("/api/project/:slug", delete(delete_project)) + .route("/api/project/:username/:slug", get(get_project)) .route("/api/search-projects", get(search_projects)) .route("/api/publish", post(publish_handler)) .route("/e/:token", get(resolve_secret_embed)) // Secret Embed Route From 51e7f5e98dc49b566c6062546970b051bd91b3d8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 16 Feb 2026 19:11:41 +0000 Subject: [PATCH 3/3] Fix embed-key endpoint to use username/slug pattern Co-authored-by: Yashb404 <139128977+Yashb404@users.noreply.github.com> --- client/src/pages/view.rs | 4 ++-- server/src/handlers/project.rs | 11 ++++++----- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/client/src/pages/view.rs b/client/src/pages/view.rs index 4df1243..ec48a03 100644 --- a/client/src/pages/view.rs +++ b/client/src/pages/view.rs @@ -439,7 +439,7 @@ pub fn ViewPage() -> impl IntoView { let slug_clone = slug(); let username_clone = username(); spawn_local(async move { - let url = format!("{}/api/project/{}/embed-key", api_base(), slug_clone); + let url = format!("{}/api/project/{}/{}/embed-key", api_base(), username_clone, slug_clone); match Request::get(&url).credentials(RequestCredentials::Include).send().await { Ok(resp) => { match resp.json::().await { @@ -492,7 +492,7 @@ pub fn ViewPage() -> impl IntoView { let slug_clone = slug(); let username_clone = username(); spawn_local(async move { - let url = format!("{}/api/project/{}/embed-key", api_base(), slug_clone); + let url = format!("{}/api/project/{}/{}/embed-key", api_base(), username_clone, slug_clone); match Request::get(&url).credentials(RequestCredentials::Include).send().await { Ok(resp) => { match resp.json::().await { diff --git a/server/src/handlers/project.rs b/server/src/handlers/project.rs index fbdcb51..67988eb 100644 --- a/server/src/handlers/project.rs +++ b/server/src/handlers/project.rs @@ -147,13 +147,13 @@ fn validate_csrf_protection(headers: &HeaderMap) -> Result<(), (StatusCode, Stri pub fn routes() -> Router { Router::new() .route("/api/my-projects", get(list_user_projects)) - .route("/api/project/:slug/embed-key", get(get_embed_key)) + .route("/api/project/:username/:slug", get(get_project)) + .route("/api/project/:username/:slug/embed-key", get(get_embed_key)) .route( "/api/project/:slug/whitelist", get(get_whitelist).post(add_to_whitelist).delete(remove_from_whitelist), ) .route("/api/project/:slug", delete(delete_project)) - .route("/api/project/:username/:slug", get(get_project)) .route("/api/search-projects", get(search_projects)) .route("/api/publish", post(publish_handler)) .route("/e/:token", get(resolve_secret_embed)) // Secret Embed Route @@ -749,7 +749,7 @@ pub async fn remove_from_whitelist( pub async fn get_embed_key( State(state): State, session: Session, - Path(slug): Path, + Path((username, slug)): Path<(String, String)>, ) -> Result, (StatusCode, String)> { // 1. Verify user is authenticated let user: Option = session @@ -758,12 +758,13 @@ pub async fn get_embed_key( .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Session Error: {}", e)))?; let user = user.ok_or((StatusCode::UNAUTHORIZED, "Unauthorized".to_string()))?; - // 2. Fetch project and verify ownership + // 2. Fetch project and verify ownership (case-insensitive for username/slug) let row_result = sqlx::query_as::<_, (i64, i64, Option)>( "SELECT id, owner_id, embed_key \ FROM projects \ - WHERE LOWER(slug) = LOWER($1)" + WHERE LOWER(owner_username) = LOWER($1) AND LOWER(slug) = LOWER($2)" ) + .bind(&username) .bind(&slug) .fetch_optional(&state.db) .await