Skip to content

Commit c4cf991

Browse files
author
Marco Napetti
committed
Allow crate version deletion in the first 24 hours from creation
1 parent 5231704 commit c4cf991

File tree

3 files changed

+73
-0
lines changed

3 files changed

+73
-0
lines changed

src/controllers/version.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
pub mod delete;
12
pub mod deprecated;
23
pub mod downloads;
34
pub mod metadata;

src/controllers/version/delete.rs

+68
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
//! Functionality related to deleting a crate.
2+
3+
use chrono::{Duration, Utc};
4+
use diesel::dsl::count_star;
5+
6+
use crate::controllers::cargo_prelude::*;
7+
8+
use crate::schema::*;
9+
use crate::util::errors::internal;
10+
use crate::views::{EncodableCrate, GoodCrate, PublishWarnings};
11+
12+
use super::version_and_crate;
13+
14+
/// Handles the `DELETE /crates/:crate_id/:version` route.
15+
///
16+
/// Actually deletion is allowed only in the first 24 hours from creation
17+
pub async fn delete(
18+
app: AppState,
19+
Path((crate_name, semver)): Path<(String, String)>,
20+
) -> AppResult<Json<GoodCrate>> {
21+
let conn = app.db_read()?;
22+
let (version, krate) = version_and_crate(&conn, &crate_name, &semver)?;
23+
24+
if Utc::now()
25+
.naive_utc()
26+
.signed_duration_since(version.created_at)
27+
> Duration::hours(24)
28+
{
29+
return Err(cargo_err(
30+
"Version deletion is allowed only in the first 24 hours from creation",
31+
));
32+
}
33+
34+
// Create a transaction on the database, if there are no errors,
35+
// commit the transactions to delete the version.
36+
// If there are no remaining versions, delete the crate
37+
conn.transaction(|| {
38+
diesel::delete(versions::table.find(version.id)).execute(&*conn)?;
39+
40+
let top_versions = krate.top_versions(&conn)?;
41+
42+
// we can't check top_versions to know if there aren't remaining versions
43+
// because it excludes yanked versions
44+
let remaining: i64 = versions::table
45+
.filter(versions::crate_id.eq(krate.id))
46+
.select(count_star())
47+
.first(&*conn)
48+
.optional()?
49+
.unwrap_or_default();
50+
if remaining <= 0 {
51+
diesel::delete(crates::table.find(krate.id)).execute(&*conn)?;
52+
53+
let uploader = app.config.uploader();
54+
uploader
55+
.delete_index(app.http_client(), &krate.name)
56+
.map_err(|e| internal(format_args!("failed to delete crate: {e}")))?;
57+
}
58+
59+
Ok(Json(GoodCrate {
60+
krate: EncodableCrate::from_minimal(krate, Some(&top_versions), None, true, None),
61+
warnings: PublishWarnings {
62+
invalid_categories: vec![],
63+
invalid_badges: vec![],
64+
other: vec![],
65+
},
66+
}))
67+
})
68+
}

src/router.rs

+4
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ pub fn build_axum_router(state: AppState) -> Router {
2525
.put(krate::owners::add_owners)
2626
.delete(krate::owners::remove_owners),
2727
)
28+
.route(
29+
"/api/v1/crates/:crate_id/:version",
30+
delete(version::delete::delete),
31+
)
2832
.route(
2933
"/api/v1/crates/:crate_id/:version/yank",
3034
delete(version::yank::yank),

0 commit comments

Comments
 (0)