4
4
//! index or cached metadata which was extracted (client side) from the
5
5
//! `Cargo.toml` file.
6
6
7
- use axum:: Json ;
8
7
use axum_extra:: json;
9
8
use 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 ;
16
9
17
10
use 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 ;
26
13
use crate :: views:: EncodableVersion ;
27
- use crate :: worker:: jobs:: { SyncToGitIndex , SyncToSparseIndex , UpdateDefaultVersion } ;
28
14
29
15
use super :: CrateVersionPath ;
30
16
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
-
41
17
/// Get crate version metadata.
42
18
#[ utoipa:: path(
43
19
get,
@@ -55,155 +31,3 @@ pub async fn find_version(state: AppState, path: CrateVersionPath) -> AppResult<
55
31
let version = EncodableVersion :: from ( version, & krate. name , published_by, actions) ;
56
32
Ok ( json ! ( { "version" : version } ) )
57
33
}
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