max / makenotwork
- Co-Authored-By
- Claude Opus 4.8 <noreply@anthropic.com>
18 files changed,
+341 insertions,
-160 deletions
| @@ -551,6 +551,10 @@ | |||
| 551 | 551 | .stdin(std::process::Stdio::null()) | |
| 552 | 552 | .stdout(std::process::Stdio::piped()) | |
| 553 | 553 | .stderr(std::process::Stdio::piped()) | |
| 554 | + | // Kill the ssh process if this future is dropped (e.g. the 30-min build | |
| 555 | + | // timeout fires): otherwise the dropped future leaves ssh — and the | |
| 556 | + | // remote build it drives — running orphaned (ultra-fuzz Run 11 Perf). | |
| 557 | + | .kill_on_drop(true) | |
| 554 | 558 | .spawn() | |
| 555 | 559 | .map_err(|e| format!("failed to spawn ssh: {e}"))?; | |
| 556 | 560 | ||
| @@ -625,6 +629,9 @@ | |||
| 625 | 629 | args.push(local_path); | |
| 626 | 630 | let output = tokio::process::Command::new("scp") | |
| 627 | 631 | .args(&args) | |
| 632 | + | // Kill scp if this future is dropped (build timeout) rather than leaving | |
| 633 | + | // an orphaned transfer running (ultra-fuzz Run 11 Perf). | |
| 634 | + | .kill_on_drop(true) | |
| 628 | 635 | .output() | |
| 629 | 636 | .await | |
| 630 | 637 | .map_err(|e| format!("failed to spawn scp: {e}"))?; |
| @@ -341,7 +341,7 @@ | |||
| 341 | 341 | p.project_type, | |
| 342 | 342 | p.created_at, | |
| 343 | 343 | u.username, | |
| 344 | - | COUNT(i.id) FILTER (WHERE i.is_public = true AND i.listed = true AND i.deleted_at IS NULL) as item_count, | |
| 344 | + | p.item_count::bigint as item_count, | |
| 345 | 345 | GREATEST( | |
| 346 | 346 | similarity(p.title, $1), | |
| 347 | 347 | similarity(COALESCE(p.description, ''), $1) * 0.5 | |
| @@ -350,7 +350,6 @@ | |||
| 350 | 350 | pc.slug as category_slug | |
| 351 | 351 | FROM projects p | |
| 352 | 352 | JOIN users u ON p.user_id = u.id | |
| 353 | - | LEFT JOIN items i ON i.project_id = p.id | |
| 354 | 353 | LEFT JOIN project_categories pc ON pc.id = p.category_id | |
| 355 | 354 | WHERE p.is_public = true AND u.is_sandbox = FALSE | |
| 356 | 355 | "#, | |
| @@ -366,13 +365,12 @@ | |||
| 366 | 365 | p.project_type, | |
| 367 | 366 | p.created_at, | |
| 368 | 367 | u.username, | |
| 369 | - | COUNT(i.id) FILTER (WHERE i.is_public = true AND i.listed = true AND i.deleted_at IS NULL) as item_count, | |
| 368 | + | p.item_count::bigint as item_count, | |
| 370 | 369 | 1.0::real as match_score, | |
| 371 | 370 | pc.name as category_name, | |
| 372 | 371 | pc.slug as category_slug | |
| 373 | 372 | FROM projects p | |
| 374 | 373 | JOIN users u ON p.user_id = u.id | |
| 375 | - | LEFT JOIN items i ON i.project_id = p.id | |
| 376 | 374 | LEFT JOIN project_categories pc ON pc.id = p.category_id | |
| 377 | 375 | WHERE p.is_public = true AND u.is_sandbox = FALSE | |
| 378 | 376 | "#, | |
| @@ -387,13 +385,12 @@ | |||
| 387 | 385 | p.project_type, | |
| 388 | 386 | p.created_at, | |
| 389 | 387 | u.username, | |
| 390 | - | COUNT(i.id) FILTER (WHERE i.is_public = true AND i.listed = true AND i.deleted_at IS NULL) as item_count, | |
| 388 | + | p.item_count::bigint as item_count, | |
| 391 | 389 | NULL::real as match_score, | |
| 392 | 390 | pc.name as category_name, | |
| 393 | 391 | pc.slug as category_slug | |
| 394 | 392 | FROM projects p | |
| 395 | 393 | JOIN users u ON p.user_id = u.id | |
| 396 | - | LEFT JOIN items i ON i.project_id = p.id | |
| 397 | 394 | LEFT JOIN project_categories pc ON pc.id = p.category_id | |
| 398 | 395 | WHERE p.is_public = true AND u.is_sandbox = FALSE | |
| 399 | 396 | "#, | |
| @@ -416,7 +413,8 @@ | |||
| 416 | 413 | query.push_str(" AND EXISTS (SELECT 1 FROM git_repos gr WHERE gr.project_id = p.id)"); | |
| 417 | 414 | } | |
| 418 | 415 | ||
| 419 | - | query.push_str(" GROUP BY p.id, u.username, pc.name, pc.slug"); | |
| 416 | + | // No GROUP BY: item_count is now a denormalized column on projects (maintained | |
| 417 | + | // by a trigger), so the query has no aggregate to group (Run 11 Perf SER-3). | |
| 420 | 418 | ||
| 421 | 419 | let order = if has_search && (sort_by.is_none() || sort_by == Some(DiscoverSort::Newest)) { | |
| 422 | 420 | "match_score DESC NULLS LAST, p.created_at DESC" |
| @@ -83,30 +83,25 @@ | |||
| 83 | 83 | ) -> Result<Vec<super::models::DbItem>> { | |
| 84 | 84 | let items = sqlx::query_as::<_, super::models::DbItem>( | |
| 85 | 85 | r#" | |
| 86 | - | SELECT DISTINCT i.* FROM items i | |
| 87 | - | JOIN projects p ON i.project_id = p.id | |
| 88 | - | WHERE i.is_public = true AND p.is_public = true | |
| 89 | - | AND ( | |
| 90 | - | -- Items from followed users | |
| 91 | - | p.user_id IN ( | |
| 92 | - | SELECT target_id FROM follows | |
| 93 | - | WHERE follower_id = $1 AND target_type = 'user' | |
| 94 | - | ) | |
| 95 | - | OR | |
| 96 | - | -- Items from followed projects | |
| 97 | - | p.id IN ( | |
| 98 | - | SELECT target_id FROM follows | |
| 99 | - | WHERE follower_id = $1 AND target_type = 'project' | |
| 100 | - | ) | |
| 101 | - | OR | |
| 102 | - | -- Items with followed tags | |
| 103 | - | i.id IN ( | |
| 104 | - | SELECT it.item_id FROM item_tags it | |
| 105 | - | WHERE it.tag_id IN ( | |
| 106 | - | SELECT target_id FROM follows WHERE follower_id = $1 AND target_type = 'tag' | |
| 107 | - | ) | |
| 108 | - | ) | |
| 86 | + | WITH followed_item_ids AS ( | |
| 87 | + | SELECT i.id FROM items i | |
| 88 | + | JOIN projects p ON i.project_id = p.id | |
| 89 | + | JOIN follows f ON f.follower_id = $1 AND f.target_type = 'user' AND f.target_id = p.user_id | |
| 90 | + | WHERE i.is_public = true AND p.is_public = true | |
| 91 | + | UNION | |
| 92 | + | SELECT i.id FROM items i | |
| 93 | + | JOIN projects p ON i.project_id = p.id | |
| 94 | + | JOIN follows f ON f.follower_id = $1 AND f.target_type = 'project' AND f.target_id = p.id | |
| 95 | + | WHERE i.is_public = true AND p.is_public = true | |
| 96 | + | UNION | |
| 97 | + | SELECT i.id FROM items i | |
| 98 | + | JOIN projects p ON i.project_id = p.id | |
| 99 | + | JOIN item_tags it ON it.item_id = i.id | |
| 100 | + | JOIN follows f ON f.follower_id = $1 AND f.target_type = 'tag' AND f.target_id = it.tag_id | |
| 101 | + | WHERE i.is_public = true AND p.is_public = true | |
| 109 | 102 | ) | |
| 103 | + | SELECT i.* FROM items i | |
| 104 | + | JOIN followed_item_ids fi ON fi.id = i.id | |
| 110 | 105 | ORDER BY i.created_at DESC | |
| 111 | 106 | LIMIT 50 | |
| 112 | 107 | "#, | |
| @@ -129,7 +124,24 @@ | |||
| 129 | 124 | ) -> Result<Vec<super::models::DbDiscoverItemRow>> { | |
| 130 | 125 | let items = sqlx::query_as::<_, super::models::DbDiscoverItemRow>( | |
| 131 | 126 | r#" | |
| 132 | - | SELECT DISTINCT | |
| 127 | + | WITH followed_item_ids AS ( | |
| 128 | + | SELECT i.id FROM items i | |
| 129 | + | JOIN projects p ON i.project_id = p.id | |
| 130 | + | JOIN follows f ON f.follower_id = $1 AND f.target_type = 'user' AND f.target_id = p.user_id | |
| 131 | + | WHERE i.is_public = true AND p.is_public = true | |
| 132 | + | UNION | |
| 133 | + | SELECT i.id FROM items i | |
| 134 | + | JOIN projects p ON i.project_id = p.id | |
| 135 | + | JOIN follows f ON f.follower_id = $1 AND f.target_type = 'project' AND f.target_id = p.id | |
| 136 | + | WHERE i.is_public = true AND p.is_public = true | |
| 137 | + | UNION | |
| 138 | + | SELECT i.id FROM items i | |
| 139 | + | JOIN projects p ON i.project_id = p.id | |
| 140 | + | JOIN item_tags it ON it.item_id = i.id | |
| 141 | + | JOIN follows f ON f.follower_id = $1 AND f.target_type = 'tag' AND f.target_id = it.tag_id | |
| 142 | + | WHERE i.is_public = true AND p.is_public = true | |
| 143 | + | ) | |
| 144 | + | SELECT | |
| 133 | 145 | i.id, | |
| 134 | 146 | i.title, | |
| 135 | 147 | i.description, | |
| @@ -145,25 +157,11 @@ | |||
| 145 | 157 | NULL::real as match_score, | |
| 146 | 158 | i.ai_tier | |
| 147 | 159 | FROM items i | |
| 160 | + | JOIN followed_item_ids fi ON fi.id = i.id | |
| 148 | 161 | JOIN projects p ON i.project_id = p.id | |
| 149 | 162 | JOIN users u ON p.user_id = u.id | |
| 150 | 163 | LEFT JOIN item_tags pit ON pit.item_id = i.id AND pit.is_primary = true | |
| 151 | 164 | LEFT JOIN tags pt ON pt.id = pit.tag_id | |
| 152 | - | WHERE i.is_public = true AND p.is_public = true | |
| 153 | - | AND ( | |
| 154 | - | p.user_id IN ( | |
| 155 | - | SELECT target_id FROM follows WHERE follower_id = $1 AND target_type = 'user' | |
| 156 | - | ) | |
| 157 | - | OR p.id IN ( | |
| 158 | - | SELECT target_id FROM follows WHERE follower_id = $1 AND target_type = 'project' | |
| 159 | - | ) | |
| 160 | - | OR i.id IN ( | |
| 161 | - | SELECT it.item_id FROM item_tags it | |
| 162 | - | WHERE it.tag_id IN ( | |
| 163 | - | SELECT target_id FROM follows WHERE follower_id = $1 AND target_type = 'tag' | |
| 164 | - | ) | |
| 165 | - | ) | |
| 166 | - | ) | |
| 167 | 165 | ORDER BY i.created_at DESC | |
| 168 | 166 | LIMIT $2 OFFSET $3 | |
| 169 | 167 | "#, | |
| @@ -185,24 +183,24 @@ | |||
| 185 | 183 | ) -> Result<i64> { | |
| 186 | 184 | let count: i64 = sqlx::query_scalar( | |
| 187 | 185 | r#" | |
| 188 | - | SELECT COUNT(DISTINCT i.id) | |
| 189 | - | FROM items i | |
| 190 | - | JOIN projects p ON i.project_id = p.id | |
| 191 | - | WHERE i.is_public = true AND p.is_public = true | |
| 192 | - | AND ( | |
| 193 | - | p.user_id IN ( | |
| 194 | - | SELECT target_id FROM follows WHERE follower_id = $1 AND target_type = 'user' | |
| 195 | - | ) | |
| 196 | - | OR p.id IN ( | |
| 197 | - | SELECT target_id FROM follows WHERE follower_id = $1 AND target_type = 'project' | |
| 198 | - | ) | |
| 199 | - | OR i.id IN ( | |
| 200 | - | SELECT it.item_id FROM item_tags it | |
| 201 | - | WHERE it.tag_id IN ( | |
| 202 | - | SELECT target_id FROM follows WHERE follower_id = $1 AND target_type = 'tag' | |
| 203 | - | ) | |
| 204 | - | ) | |
| 186 | + | WITH followed_item_ids AS ( | |
| 187 | + | SELECT i.id FROM items i | |
| 188 | + | JOIN projects p ON i.project_id = p.id | |
| 189 | + | JOIN follows f ON f.follower_id = $1 AND f.target_type = 'user' AND f.target_id = p.user_id | |
| 190 | + | WHERE i.is_public = true AND p.is_public = true | |
| 191 | + | UNION | |
| 192 | + | SELECT i.id FROM items i | |
| 193 | + | JOIN projects p ON i.project_id = p.id | |
| 194 | + | JOIN follows f ON f.follower_id = $1 AND f.target_type = 'project' AND f.target_id = p.id | |
| 195 | + | WHERE i.is_public = true AND p.is_public = true | |
| 196 | + | UNION | |
| 197 | + | SELECT i.id FROM items i | |
| 198 | + | JOIN projects p ON i.project_id = p.id | |
| 199 | + | JOIN item_tags it ON it.item_id = i.id | |
| 200 | + | JOIN follows f ON f.follower_id = $1 AND f.target_type = 'tag' AND f.target_id = it.tag_id | |
| 201 | + | WHERE i.is_public = true AND p.is_public = true | |
| 205 | 202 | ) | |
| 203 | + | SELECT COUNT(*) FROM followed_item_ids | |
| 206 | 204 | "#, | |
| 207 | 205 | ) | |
| 208 | 206 | .bind(follower_id) |
| @@ -110,6 +110,27 @@ | |||
| 110 | 110 | Ok(release) | |
| 111 | 111 | } | |
| 112 | 112 | ||
| 113 | + | /// Fetch a single release scoped to its app — a direct indexed `(id, app_id)` | |
| 114 | + | /// lookup instead of listing the app's whole release set and scanning it in Rust | |
| 115 | + | /// (ultra-fuzz Run 11 Perf SER-1). Returns None if the release doesn't exist or | |
| 116 | + | /// doesn't belong to the app. | |
| 117 | + | #[tracing::instrument(skip_all)] | |
| 118 | + | pub async fn get_release( | |
| 119 | + | pool: &PgPool, | |
| 120 | + | app_id: SyncAppId, | |
| 121 | + | release_id: OtaReleaseId, | |
| 122 | + | ) -> Result<Option<DbOtaRelease>> { | |
| 123 | + | let release = sqlx::query_as::<_, DbOtaRelease>( | |
| 124 | + | "SELECT * FROM ota_releases WHERE id = $1 AND app_id = $2", | |
| 125 | + | ) | |
| 126 | + | .bind(release_id) | |
| 127 | + | .bind(app_id) | |
| 128 | + | .fetch_optional(pool) | |
| 129 | + | .await?; | |
| 130 | + | ||
| 131 | + | Ok(release) | |
| 132 | + | } | |
| 133 | + | ||
| 113 | 134 | /// Delete a release (cascades to artifacts). | |
| 114 | 135 | #[tracing::instrument(skip_all)] | |
| 115 | 136 | pub async fn delete_release(pool: &PgPool, release_id: OtaReleaseId) -> Result<bool> { |
| @@ -280,11 +280,9 @@ | |||
| 280 | 280 | return Err(AppError::BadRequest("file_size must be positive".to_string())); | |
| 281 | 281 | } | |
| 282 | 282 | ||
| 283 | - | // Verify the release belongs to this app | |
| 284 | - | let releases = db::ota::list_releases(&state.db, app_id).await?; | |
| 285 | - | let release = releases | |
| 286 | - | .iter() | |
| 287 | - | .find(|r| r.id == release_id) | |
| 283 | + | // Verify the release belongs to this app (scoped lookup, not a full list scan) | |
| 284 | + | let release = db::ota::get_release(&state.db, app_id, release_id) | |
| 285 | + | .await? | |
| 288 | 286 | .ok_or(AppError::NotFound)?; | |
| 289 | 287 | ||
| 290 | 288 | let s3_key = crate::storage::S3Client::generate_ota_artifact_key( | |
| @@ -390,9 +388,11 @@ | |||
| 390 | 388 | .await? | |
| 391 | 389 | .ok_or(AppError::NotFound)?; | |
| 392 | 390 | ||
| 393 | - | // Verify release belongs to this app | |
| 394 | - | let releases = db::ota::list_releases(&state.db, app.id).await?; | |
| 395 | - | if !releases.iter().any(|r| r.id == release_id) { | |
| 391 | + | // Verify release belongs to this app (scoped lookup, not a full list scan) | |
| 392 | + | if db::ota::get_release(&state.db, app.id, release_id) | |
| 393 | + | .await? | |
| 394 | + | .is_none() | |
| 395 | + | { | |
| 396 | 396 | return Err(AppError::NotFound); | |
| 397 | 397 | } | |
| 398 | 398 |
| @@ -196,6 +196,7 @@ | |||
| 196 | 196 | ProjectAnalyticsTabTemplate, | |
| 197 | 197 | UserAnalyticsTabTemplate, | |
| 198 | 198 | BuyerContactsPartialTemplate, | |
| 199 | + | PayoutSummaryPartialTemplate, | |
| 199 | 200 | ProjectSettingsTabTemplate, | |
| 200 | 201 | ProjectCodeTabTemplate, | |
| 201 | 202 | ProjectBlogTabTemplate, |
| @@ -262,7 +262,6 @@ | |||
| 262 | 262 | #[template(path = "partials/tabs/user_payments.html")] | |
| 263 | 263 | pub struct UserPaymentsTabTemplate { | |
| 264 | 264 | pub user: User, | |
| 265 | - | pub payout_summary: Option<PayoutSummary>, | |
| 266 | 265 | pub transactions: Vec<Transaction>, | |
| 267 | 266 | pub tips_received: Vec<TipReceived>, | |
| 268 | 267 | pub tips_total: String, | |
| @@ -620,6 +619,15 @@ | |||
| 620 | 619 | pub contacts: Vec<BuyerContact>, | |
| 621 | 620 | } | |
| 622 | 621 | ||
| 622 | + | /// Stripe payout-summary card, HTMX-loaded into the Payments tab so the tab | |
| 623 | + | /// render never blocks on the Stripe balance round-trip (ultra-fuzz Run 11 Perf SER-2). | |
| 624 | + | #[derive(Template)] | |
| 625 | + | #[template(path = "partials/tabs/payout_summary.html")] | |
| 626 | + | pub struct PayoutSummaryPartialTemplate { | |
| 627 | + | pub payout_summary: Option<PayoutSummary>, | |
| 628 | + | pub stripe_payouts_enabled: bool, | |
| 629 | + | } | |
| 630 | + | ||
| 623 | 631 | /// SSH keys list partial for HTMX updates. | |
| 624 | 632 | #[derive(Template)] | |
| 625 | 633 | #[template(path = "partials/ssh_keys_list.html")] |