Skip to content

Commit 41dc490

Browse files
authored
Merge pull request #10231 from Turbo87/controller-modules
Extract more controller modules
2 parents f94e5e0 + dc70376 commit 41dc490

18 files changed

+492
-445
lines changed

src/controllers.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ pub mod github;
88
pub mod keyword;
99
pub mod krate;
1010
pub mod metrics;
11+
pub mod session;
1112
pub mod site_metadata;
1213
pub mod summary;
1314
pub mod team;

src/controllers/krate.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ pub mod follow;
1212
pub mod metadata;
1313
pub mod owners;
1414
pub mod publish;
15+
pub mod rev_deps;
1516
pub mod search;
1617
pub mod versions;
1718

src/controllers/krate/metadata.rs

Lines changed: 4 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,15 @@
55
//! `Cargo.toml` file.
66
77
use crate::app::AppState;
8-
use crate::controllers::helpers::pagination::PaginationOptions;
98
use crate::controllers::krate::CratePath;
10-
use crate::controllers::version::CrateVersionPath;
119
use crate::models::{
12-
Category, Crate, CrateCategory, CrateKeyword, CrateName, Keyword, RecentCrateDownloads, User,
13-
Version, VersionOwnerAction,
10+
Category, Crate, CrateCategory, CrateKeyword, Keyword, RecentCrateDownloads, User, Version,
11+
VersionOwnerAction,
1412
};
1513
use crate::schema::*;
1614
use crate::util::errors::{bad_request, crate_not_found, AppResult, BoxedAppError};
17-
use crate::util::{redirect, RequestUtils};
18-
use crate::views::{
19-
EncodableCategory, EncodableCrate, EncodableDependency, EncodableKeyword, EncodableVersion,
20-
};
21-
use axum::response::{IntoResponse, Response};
15+
use crate::util::RequestUtils;
16+
use crate::views::{EncodableCategory, EncodableCrate, EncodableKeyword, EncodableVersion};
2217
use axum_extra::json;
2318
use axum_extra::response::ErasedJson;
2419
use diesel::prelude::*;
@@ -244,80 +239,3 @@ impl FromStr for ShowIncludeMode {
244239
Ok(mode)
245240
}
246241
}
247-
248-
/// Get the readme of a crate version.
249-
#[utoipa::path(
250-
get,
251-
path = "/api/v1/crates/{name}/{version}/readme",
252-
params(CrateVersionPath),
253-
tag = "versions",
254-
responses((status = 200, description = "Successful Response")),
255-
)]
256-
pub async fn get_version_readme(app: AppState, path: CrateVersionPath, req: Parts) -> Response {
257-
let redirect_url = app.storage.readme_location(&path.name, &path.version);
258-
if req.wants_json() {
259-
json!({ "url": redirect_url }).into_response()
260-
} else {
261-
redirect(redirect_url)
262-
}
263-
}
264-
265-
/// List reverse dependencies of a crate.
266-
#[utoipa::path(
267-
get,
268-
path = "/api/v1/crates/{name}/reverse_dependencies",
269-
params(CratePath),
270-
tag = "crates",
271-
responses((status = 200, description = "Successful Response")),
272-
)]
273-
pub async fn list_reverse_dependencies(
274-
app: AppState,
275-
path: CratePath,
276-
req: Parts,
277-
) -> AppResult<ErasedJson> {
278-
let mut conn = app.db_read().await?;
279-
280-
let pagination_options = PaginationOptions::builder().gather(&req)?;
281-
282-
let krate = path.load_crate(&mut conn).await?;
283-
284-
let (rev_deps, total) = krate
285-
.reverse_dependencies(&mut conn, pagination_options)
286-
.await?;
287-
288-
let rev_deps: Vec<_> = rev_deps
289-
.into_iter()
290-
.map(|dep| EncodableDependency::from_reverse_dep(dep, &krate.name))
291-
.collect();
292-
293-
let version_ids: Vec<i32> = rev_deps.iter().map(|dep| dep.version_id).collect();
294-
295-
let versions_and_publishers: Vec<(Version, CrateName, Option<User>)> = versions::table
296-
.filter(versions::id.eq_any(version_ids))
297-
.inner_join(crates::table)
298-
.left_outer_join(users::table)
299-
.select(<(Version, CrateName, Option<User>)>::as_select())
300-
.load(&mut conn)
301-
.await?;
302-
303-
let versions = versions_and_publishers
304-
.iter()
305-
.map(|(v, ..)| v)
306-
.collect::<Vec<_>>();
307-
308-
let actions = VersionOwnerAction::for_versions(&mut conn, &versions).await?;
309-
310-
let versions = versions_and_publishers
311-
.into_iter()
312-
.zip(actions)
313-
.map(|((version, krate_name, published_by), actions)| {
314-
EncodableVersion::from(version, &krate_name.name, published_by, actions)
315-
})
316-
.collect::<Vec<_>>();
317-
318-
Ok(json!({
319-
"dependencies": rev_deps,
320-
"versions": versions,
321-
"meta": { "total": total },
322-
}))
323-
}

src/controllers/krate/rev_deps.rs

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
use crate::app::AppState;
2+
use crate::controllers::helpers::pagination::PaginationOptions;
3+
use crate::controllers::krate::CratePath;
4+
use crate::models::{CrateName, User, Version, VersionOwnerAction};
5+
use crate::util::errors::AppResult;
6+
use crate::views::{EncodableDependency, EncodableVersion};
7+
use axum_extra::json;
8+
use axum_extra::response::ErasedJson;
9+
use crates_io_database::schema::{crates, users, versions};
10+
use diesel::prelude::*;
11+
use diesel_async::RunQueryDsl;
12+
use http::request::Parts;
13+
14+
/// List reverse dependencies of a crate.
15+
#[utoipa::path(
16+
get,
17+
path = "/api/v1/crates/{name}/reverse_dependencies",
18+
params(CratePath),
19+
tag = "crates",
20+
responses((status = 200, description = "Successful Response")),
21+
)]
22+
pub async fn list_reverse_dependencies(
23+
app: AppState,
24+
path: CratePath,
25+
req: Parts,
26+
) -> AppResult<ErasedJson> {
27+
let mut conn = app.db_read().await?;
28+
29+
let pagination_options = PaginationOptions::builder().gather(&req)?;
30+
31+
let krate = path.load_crate(&mut conn).await?;
32+
33+
let (rev_deps, total) = krate
34+
.reverse_dependencies(&mut conn, pagination_options)
35+
.await?;
36+
37+
let rev_deps: Vec<_> = rev_deps
38+
.into_iter()
39+
.map(|dep| EncodableDependency::from_reverse_dep(dep, &krate.name))
40+
.collect();
41+
42+
let version_ids: Vec<i32> = rev_deps.iter().map(|dep| dep.version_id).collect();
43+
44+
let versions_and_publishers: Vec<(Version, CrateName, Option<User>)> = versions::table
45+
.filter(versions::id.eq_any(version_ids))
46+
.inner_join(crates::table)
47+
.left_outer_join(users::table)
48+
.select(<(Version, CrateName, Option<User>)>::as_select())
49+
.load(&mut conn)
50+
.await?;
51+
52+
let versions = versions_and_publishers
53+
.iter()
54+
.map(|(v, ..)| v)
55+
.collect::<Vec<_>>();
56+
57+
let actions = VersionOwnerAction::for_versions(&mut conn, &versions).await?;
58+
59+
let versions = versions_and_publishers
60+
.into_iter()
61+
.zip(actions)
62+
.map(|((version, krate_name, published_by), actions)| {
63+
EncodableVersion::from(version, &krate_name.name, published_by, actions)
64+
})
65+
.collect::<Vec<_>>();
66+
67+
Ok(json!({
68+
"dependencies": rev_deps,
69+
"versions": versions,
70+
"meta": { "total": total },
71+
}))
72+
}

src/controllers/user/session.rs renamed to src/controllers/session.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ pub async fn authorize_session(
128128
// Log in by setting a cookie and the middleware authentication
129129
session.insert("user_id".to_string(), user.id.to_string());
130130

131-
super::me::get_authenticated_user(app, req).await
131+
super::user::me::get_authenticated_user(app, req).await
132132
}
133133

134134
async fn save_user_to_database(

src/controllers/user.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1+
pub mod email_notifications;
2+
pub mod email_verification;
13
pub mod me;
24
pub mod other;
3-
pub mod resend;
4-
pub mod session;
55
pub mod update;
66

7-
pub use resend::resend_email_verification;
7+
pub use email_verification::resend_email_verification;
88
pub use update::update_user;
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
use crate::app::AppState;
2+
use crate::auth::AuthCheck;
3+
use crate::controllers::helpers::ok_true;
4+
use crate::models::{CrateOwner, OwnerKind};
5+
use crate::schema::crate_owners;
6+
use crate::util::errors::AppResult;
7+
use axum::response::Response;
8+
use axum::Json;
9+
use diesel::prelude::*;
10+
use diesel_async::RunQueryDsl;
11+
use http::request::Parts;
12+
use std::collections::HashMap;
13+
14+
#[derive(Deserialize)]
15+
pub struct CrateEmailNotifications {
16+
id: i32,
17+
email_notifications: bool,
18+
}
19+
20+
/// Update email notification settings for the authenticated user.
21+
///
22+
/// This endpoint was implemented for an experimental feature that was never
23+
/// fully implemented. It is now deprecated and will be removed in the future.
24+
#[utoipa::path(
25+
put,
26+
path = "/api/v1/me/email_notifications",
27+
tag = "users",
28+
responses((status = 200, description = "Successful Response")),
29+
)]
30+
#[deprecated]
31+
pub async fn update_email_notifications(
32+
app: AppState,
33+
parts: Parts,
34+
Json(updates): Json<Vec<CrateEmailNotifications>>,
35+
) -> AppResult<Response> {
36+
use diesel::pg::upsert::excluded;
37+
38+
let updates: HashMap<i32, bool> = updates
39+
.iter()
40+
.map(|c| (c.id, c.email_notifications))
41+
.collect();
42+
43+
let mut conn = app.db_write().await?;
44+
let user_id = AuthCheck::default()
45+
.check(&parts, &mut conn)
46+
.await?
47+
.user_id();
48+
49+
// Build inserts from existing crates belonging to the current user
50+
let to_insert = CrateOwner::by_owner_kind(OwnerKind::User)
51+
.filter(crate_owners::owner_id.eq(user_id))
52+
.select((
53+
crate_owners::crate_id,
54+
crate_owners::owner_id,
55+
crate_owners::owner_kind,
56+
crate_owners::email_notifications,
57+
))
58+
.load(&mut conn)
59+
.await?
60+
.into_iter()
61+
// Remove records whose `email_notifications` will not change from their current value
62+
.map(
63+
|(c_id, o_id, o_kind, e_notifications): (i32, i32, i32, bool)| {
64+
let current_e_notifications = *updates.get(&c_id).unwrap_or(&e_notifications);
65+
(
66+
crate_owners::crate_id.eq(c_id),
67+
crate_owners::owner_id.eq(o_id),
68+
crate_owners::owner_kind.eq(o_kind),
69+
crate_owners::email_notifications.eq(current_e_notifications),
70+
)
71+
},
72+
)
73+
.collect::<Vec<_>>();
74+
75+
// Upsert crate owners; this should only actually execute updates
76+
diesel::insert_into(crate_owners::table)
77+
.values(&to_insert)
78+
.on_conflict((
79+
crate_owners::crate_id,
80+
crate_owners::owner_id,
81+
crate_owners::owner_kind,
82+
))
83+
.do_update()
84+
.set(crate_owners::email_notifications.eq(excluded(crate_owners::email_notifications)))
85+
.execute(&mut conn)
86+
.await?;
87+
88+
ok_true()
89+
}

src/controllers/user/resend.rs renamed to src/controllers/user/email_verification.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,31 @@ use diesel_async::scoped_futures::ScopedFutureExt;
1414
use diesel_async::{AsyncConnection, RunQueryDsl};
1515
use http::request::Parts;
1616

17+
/// Marks the email belonging to the given token as verified.
18+
#[utoipa::path(
19+
put,
20+
path = "/api/v1/confirm/{email_token}",
21+
params(
22+
("email_token" = String, Path, description = "Secret verification token sent to the user's email address"),
23+
),
24+
tag = "users",
25+
responses((status = 200, description = "Successful Response")),
26+
)]
27+
pub async fn confirm_user_email(state: AppState, Path(token): Path<String>) -> AppResult<Response> {
28+
let mut conn = state.db_write().await?;
29+
30+
let updated_rows = diesel::update(emails::table.filter(emails::token.eq(&token)))
31+
.set(emails::verified.eq(true))
32+
.execute(&mut conn)
33+
.await?;
34+
35+
if updated_rows == 0 {
36+
return Err(bad_request("Email belonging to token not found."));
37+
}
38+
39+
ok_true()
40+
}
41+
1742
/// Regenerate and send an email verification token.
1843
#[utoipa::path(
1944
put,

0 commit comments

Comments
 (0)