44//! index or cached metadata which was extracted (client side) from the
55//! `Cargo.toml` file.
66
7- use axum:: Json ;
87use axum_extra:: json;
98use axum_extra:: response:: ErasedJson ;
10- use crates_io_worker:: BackgroundJob ;
11- use diesel:: prelude:: * ;
12- use diesel_async:: { AsyncPgConnection , RunQueryDsl } ;
13- use http:: request:: Parts ;
14- use http:: StatusCode ;
15- use serde:: Deserialize ;
169
1710use crate :: app:: AppState ;
18- use crate :: auth:: { AuthCheck , Authentication } ;
19- use crate :: models:: token:: EndpointScope ;
20- use crate :: models:: {
21- Crate , NewVersionOwnerAction , Rights , Version , VersionAction , VersionOwnerAction ,
22- } ;
23- use crate :: rate_limiter:: LimitedAction ;
24- use crate :: schema:: versions;
25- use crate :: util:: errors:: { bad_request, custom, AppResult } ;
11+ use crate :: models:: VersionOwnerAction ;
12+ use crate :: util:: errors:: AppResult ;
2613use crate :: views:: EncodableVersion ;
27- use crate :: worker:: jobs:: { SyncToGitIndex , SyncToSparseIndex , UpdateDefaultVersion } ;
2814
2915use super :: CrateVersionPath ;
3016
31- #[ derive( Deserialize ) ]
32- pub struct VersionUpdate {
33- yanked : Option < bool > ,
34- yank_message : Option < String > ,
35- }
36- #[ derive( Deserialize ) ]
37- pub struct VersionUpdateRequest {
38- version : VersionUpdate ,
39- }
40-
4117/// Get crate version metadata.
4218#[ utoipa:: path(
4319 get,
@@ -55,155 +31,3 @@ pub async fn find_version(state: AppState, path: CrateVersionPath) -> AppResult<
5531 let version = EncodableVersion :: from ( version, & krate. name , published_by, actions) ;
5632 Ok ( json ! ( { "version" : version } ) )
5733}
58-
59- /// Update a crate version.
60- ///
61- /// This endpoint allows updating the `yanked` state of a version, including a yank message.
62- #[ utoipa:: path(
63- patch,
64- path = "/api/v1/crates/{name}/{version}" ,
65- params( CrateVersionPath ) ,
66- tag = "versions" ,
67- responses( ( status = 200 , description = "Successful Response" ) ) ,
68- ) ]
69- pub async fn update_version (
70- state : AppState ,
71- path : CrateVersionPath ,
72- req : Parts ,
73- Json ( update_request) : Json < VersionUpdateRequest > ,
74- ) -> AppResult < ErasedJson > {
75- let mut conn = state. db_write ( ) . await ?;
76- let ( mut version, krate) = path. load_version_and_crate ( & mut conn) . await ?;
77- validate_yank_update ( & update_request. version , & version) ?;
78- let auth = authenticate ( & req, & mut conn, & krate. name ) . await ?;
79-
80- state
81- . rate_limiter
82- . check_rate_limit ( auth. user_id ( ) , LimitedAction :: YankUnyank , & mut conn)
83- . await ?;
84-
85- perform_version_yank_update (
86- & state,
87- & mut conn,
88- & mut version,
89- & krate,
90- & auth,
91- update_request. version . yanked ,
92- update_request. version . yank_message ,
93- )
94- . await ?;
95-
96- let published_by = version. published_by ( & mut conn) . await ?;
97- let actions = VersionOwnerAction :: by_version ( & mut conn, & version) . await ?;
98- let updated_version = EncodableVersion :: from ( version, & krate. name , published_by, actions) ;
99- Ok ( json ! ( { "version" : updated_version } ) )
100- }
101-
102- fn validate_yank_update ( update_data : & VersionUpdate , version : & Version ) -> AppResult < ( ) > {
103- if update_data. yank_message . is_some ( ) {
104- if matches ! ( update_data. yanked, Some ( false ) ) {
105- return Err ( bad_request ( "Cannot set yank message when unyanking" ) ) ;
106- }
107-
108- if update_data. yanked . is_none ( ) && !version. yanked {
109- return Err ( bad_request (
110- "Cannot update yank message for a version that is not yanked" ,
111- ) ) ;
112- }
113- }
114-
115- Ok ( ( ) )
116- }
117-
118- pub async fn authenticate (
119- req : & Parts ,
120- conn : & mut AsyncPgConnection ,
121- name : & str ,
122- ) -> AppResult < Authentication > {
123- AuthCheck :: default ( )
124- . with_endpoint_scope ( EndpointScope :: Yank )
125- . for_crate ( name)
126- . check ( req, conn)
127- . await
128- }
129-
130- pub async fn perform_version_yank_update (
131- state : & AppState ,
132- conn : & mut AsyncPgConnection ,
133- version : & mut Version ,
134- krate : & Crate ,
135- auth : & Authentication ,
136- yanked : Option < bool > ,
137- yank_message : Option < String > ,
138- ) -> AppResult < ( ) > {
139- let api_token_id = auth. api_token_id ( ) ;
140- let user = auth. user ( ) ;
141- let owners = krate. owners ( conn) . await ?;
142-
143- let yanked = yanked. unwrap_or ( version. yanked ) ;
144-
145- if user. rights ( state, & owners) . await ? < Rights :: Publish {
146- if user. is_admin {
147- let action = if yanked { "yanking" } else { "unyanking" } ;
148- warn ! (
149- "Admin {} is {action} {}@{}" ,
150- user. gh_login, krate. name, version. num
151- ) ;
152- } else {
153- return Err ( custom (
154- StatusCode :: FORBIDDEN ,
155- "must already be an owner to yank or unyank" ,
156- ) ) ;
157- }
158- }
159-
160- // Check if the yanked state or yank message has changed and update if necessary
161- let updated_cnt = diesel:: update (
162- versions:: table. find ( version. id ) . filter (
163- versions:: yanked
164- . is_distinct_from ( yanked)
165- . or ( versions:: yank_message. is_distinct_from ( & yank_message) ) ,
166- ) ,
167- )
168- . set ( (
169- versions:: yanked. eq ( yanked) ,
170- versions:: yank_message. eq ( & yank_message) ,
171- ) )
172- . execute ( conn)
173- . await ?;
174-
175- // If no rows were updated, return early
176- if updated_cnt == 0 {
177- return Ok ( ( ) ) ;
178- }
179-
180- // Apply the update to the version
181- version. yanked = yanked;
182- version. yank_message = yank_message;
183-
184- let action = if yanked {
185- VersionAction :: Yank
186- } else {
187- VersionAction :: Unyank
188- } ;
189- NewVersionOwnerAction :: builder ( )
190- . version_id ( version. id )
191- . user_id ( user. id )
192- . maybe_api_token_id ( api_token_id)
193- . action ( action)
194- . build ( )
195- . insert ( conn)
196- . await ?;
197-
198- let git_index_job = SyncToGitIndex :: new ( & krate. name ) ;
199- let sparse_index_job = SyncToSparseIndex :: new ( & krate. name ) ;
200- let update_default_version_job = UpdateDefaultVersion :: new ( krate. id ) ;
201-
202- tokio:: try_join!(
203- git_index_job. enqueue( conn) ,
204- sparse_index_job. enqueue( conn) ,
205- update_default_version_job. enqueue( conn) ,
206- ) ?;
207-
208- Ok ( ( ) )
209- }
0 commit comments