7
7
use axum:: extract:: Path ;
8
8
use axum:: Json ;
9
9
use crates_io_worker:: BackgroundJob ;
10
- use diesel:: { ExpressionMethods , RunQueryDsl } ;
10
+ use diesel:: { ExpressionMethods , QueryDsl , RunQueryDsl } ;
11
11
use diesel_async:: async_connection_wrapper:: AsyncConnectionWrapper ;
12
12
use http:: request:: Parts ;
13
13
use http:: StatusCode ;
@@ -22,6 +22,7 @@ use crate::models::{
22
22
insert_version_owner_action, Crate , Rights , Version , VersionAction , VersionOwnerAction ,
23
23
} ;
24
24
use crate :: rate_limiter:: LimitedAction ;
25
+ use crate :: schema:: versions;
25
26
use crate :: tasks:: spawn_blocking;
26
27
use crate :: util:: diesel:: Conn ;
27
28
use crate :: util:: errors:: { bad_request, custom, version_not_found, AppResult } ;
@@ -142,14 +143,52 @@ fn apply_yank_update(
142
143
) -> AppResult < ( ) > {
143
144
// Try to update the yank state first, to avoid unnecessary checks.
144
145
update_version_yank_state ( version, update_data) ?;
146
+ perform_version_yank_update ( state, req, conn, version, krate) ?;
145
147
146
- // Add authentication check
148
+ Ok ( ( ) )
149
+ }
150
+
151
+ fn update_version_yank_state ( version : & mut Version , update_data : & VersionUpdate ) -> AppResult < ( ) > {
152
+ match ( update_data. yanked , & update_data. yank_message ) {
153
+ ( Some ( true ) , Some ( message) ) => {
154
+ version. yanked = true ;
155
+ version. yank_message = Some ( message. clone ( ) ) ;
156
+ }
157
+ ( Some ( yanked) , None ) => {
158
+ version. yanked = yanked;
159
+ version. yank_message = None ;
160
+ }
161
+ ( Some ( false ) , Some ( _) ) => {
162
+ return Err ( bad_request ( "Cannot set yank message when unyanking" ) ) ;
163
+ }
164
+ ( None , Some ( message) ) => {
165
+ if version. yanked {
166
+ version. yank_message = Some ( message. clone ( ) ) ;
167
+ } else {
168
+ return Err ( bad_request (
169
+ "Cannot update yank message for a version that is not yanked" ,
170
+ ) ) ;
171
+ }
172
+ }
173
+ // If both yanked and yank_message are None, do nothing.
174
+ // This function only cares about updating the yanked state and yank message.
175
+ ( None , None ) => { }
176
+ }
177
+ Ok ( ( ) )
178
+ }
179
+
180
+ pub fn perform_version_yank_update (
181
+ state : & AppState ,
182
+ req : & Parts ,
183
+ conn : & mut impl Conn ,
184
+ version : & Version ,
185
+ krate : & Crate ,
186
+ ) -> AppResult < ( ) > {
147
187
let auth = AuthCheck :: default ( )
148
188
. with_endpoint_scope ( EndpointScope :: Yank )
149
189
. for_crate ( & krate. name )
150
190
. check ( req, conn) ?;
151
191
152
- // Add rate limiting check
153
192
state
154
193
. rate_limiter
155
194
. check_rate_limit ( auth. user_id ( ) , LimitedAction :: YankUnyank , conn) ?;
@@ -158,68 +197,52 @@ fn apply_yank_update(
158
197
let user = auth. user ( ) ;
159
198
let owners = krate. owners ( conn) ?;
160
199
161
- // Check user rights
162
200
if Handle :: current ( ) . block_on ( user. rights ( state, & owners) ) ? < Rights :: Publish {
163
201
if user. is_admin {
202
+ let action = if version. yanked {
203
+ "yanking"
204
+ } else {
205
+ "unyanking"
206
+ } ;
164
207
warn ! (
165
- "Admin {} is updating {}@{}" ,
208
+ "Admin {} is {action} {}@{}" ,
166
209
user. gh_login, krate. name, version. num
167
210
) ;
168
211
} else {
169
212
return Err ( custom (
170
213
StatusCode :: FORBIDDEN ,
171
- "must already be an owner to update version " ,
214
+ "must already be an owner to yank or unyank " ,
172
215
) ) ;
173
216
}
174
217
}
175
218
176
- diesel:: update ( & * version)
219
+ // Check if the yanked state or yank message has changed
220
+ let ( yanked, yank_message) = versions:: table
221
+ . find ( version. id )
222
+ . select ( ( versions:: yanked, versions:: yank_message) )
223
+ . first :: < ( bool , Option < String > ) > ( conn) ?;
224
+
225
+ if yanked == version. yanked && yank_message == version. yank_message {
226
+ // No changes, return early
227
+ return Ok ( ( ) ) ;
228
+ }
229
+
230
+ diesel:: update ( version)
177
231
. set ( (
178
- crate :: schema :: versions:: yanked. eq ( version. yanked ) ,
179
- crate :: schema :: versions:: yank_message. eq ( & version. yank_message ) ,
232
+ versions:: yanked. eq ( version. yanked ) ,
233
+ versions:: yank_message. eq ( & version. yank_message ) ,
180
234
) )
181
235
. execute ( conn) ?;
182
236
183
- // Add version owner action
184
237
let action = if version. yanked {
185
238
VersionAction :: Yank
186
239
} else {
187
240
VersionAction :: Unyank
188
241
} ;
189
242
insert_version_owner_action ( conn, version. id , user. id , api_token_id, action) ?;
190
243
191
- // Enqueue jobs
192
244
jobs:: enqueue_sync_to_index ( & krate. name , conn) ?;
193
245
UpdateDefaultVersion :: new ( krate. id ) . enqueue ( conn) ?;
194
246
195
247
Ok ( ( ) )
196
248
}
197
-
198
- fn update_version_yank_state ( version : & mut Version , update_data : & VersionUpdate ) -> AppResult < ( ) > {
199
- match ( update_data. yanked , & update_data. yank_message ) {
200
- ( Some ( true ) , Some ( message) ) => {
201
- version. yanked = true ;
202
- version. yank_message = Some ( message. clone ( ) ) ;
203
- }
204
- ( Some ( yanked) , None ) => {
205
- version. yanked = yanked;
206
- version. yank_message = None ;
207
- }
208
- ( Some ( false ) , Some ( _) ) => {
209
- return Err ( bad_request ( "Cannot set yank message when unyanking" ) ) ;
210
- }
211
- ( None , Some ( message) ) => {
212
- if version. yanked {
213
- version. yank_message = Some ( message. clone ( ) ) ;
214
- } else {
215
- return Err ( bad_request (
216
- "Cannot update yank message for a version that is not yanked" ,
217
- ) ) ;
218
- }
219
- }
220
- // If both yanked and yank_message are None, do nothing.
221
- // This function only cares about updating the yanked state and yank message.
222
- ( None , None ) => { }
223
- }
224
- Ok ( ( ) )
225
- }
0 commit comments