1use std::collections::BTreeMap;
2use std::path::Path;
3
4use chrono::{DateTime, Utc};
5use kellnr_common::crate_data::{CrateData, CrateRegistryDep, CrateVersionData};
6use kellnr_common::crate_overview::CrateOverview;
7use kellnr_common::cratesio_prefetch_msg::{CratesioPrefetchMsg, UpdateData};
8use kellnr_common::index_metadata::{IndexDep, IndexMetadata};
9use kellnr_common::normalized_name::NormalizedName;
10use kellnr_common::original_name::OriginalName;
11use kellnr_common::prefetch::Prefetch;
12use kellnr_common::publish_metadata::PublishMetadata;
13use kellnr_common::version::Version;
14use kellnr_common::webhook::{Webhook, WebhookEvent, WebhookQueue};
15use kellnr_entity::prelude::*;
16use kellnr_entity::{
17 auth_token, crate_author, crate_author_to_crate, crate_category, crate_category_to_crate,
18 crate_group, crate_index, crate_keyword, crate_keyword_to_crate, crate_meta, crate_user,
19 cratesio_crate, cratesio_index, cratesio_meta, doc_queue, group, group_user, krate, owner,
20 session, user, webhook, webhook_queue,
21};
22use kellnr_migration::iden::{
23 AuthTokenIden, CrateIden, CrateMetaIden, CratesIoIden, CratesIoMetaIden, GroupIden,
24};
25use sea_orm::entity::prelude::Uuid;
26use sea_orm::prelude::async_trait::async_trait;
27use sea_orm::query::{QueryOrder, QuerySelect, TransactionTrait};
28use sea_orm::sea_query::{Alias, Cond, Expr, JoinType, Order, Query, UnionType};
29use sea_orm::{
30 ActiveModelTrait, ActiveValue, ColumnTrait, ConnectionTrait, DatabaseConnection, EntityTrait,
31 ExprTrait, FromQueryResult, InsertResult, ModelTrait, QueryFilter, RelationTrait, Set,
32};
33
34use crate::error::DbError;
35use crate::password::{generate_salt, hash_pwd, hash_token};
36use crate::provider::{DbResult, PrefetchState};
37use crate::tables::init_database;
38use crate::{
39 AuthToken, ConString, CrateMeta, CrateSummary, DbProvider, DocQueueEntry, Group, User,
40};
41
42const DB_DATE_FORMAT: &str = "%Y-%m-%d %H:%M:%S";
43
44pub struct Database {
45 db_con: DatabaseConnection,
46}
47
48impl Database {
49 pub fn existing(db_con: DatabaseConnection) -> Self {
50 Self { db_con }
51 }
52
53 pub async fn new(con: &ConString, max_con: u32) -> Result<Self, DbError> {
54 let db_con = init_database(con, max_con)
55 .await
56 .map_err(|e| DbError::InitializationError(e.to_string()))?;
57
58 if no_user_exists(&db_con).await? {
59 insert_admin_credentials(&db_con, con).await?;
60 }
61
62 Ok(Self { db_con })
63 }
64}
65
66#[async_trait]
67impl DbProvider for Database {
68 async fn get_total_unique_cached_crates(&self) -> DbResult<u64> {
69 #[derive(Debug, PartialEq, FromQueryResult)]
70 struct SelectResult {
71 count: Option<i64>,
72 }
73
74 let stmt = Query::select()
75 .expr_as(
76 Expr::col((CratesIoIden::Table, CratesIoIden::Id)).count(),
77 Alias::new("count"),
78 )
79 .from(CratesIoIden::Table)
80 .to_owned();
81
82 let builder = self.db_con.get_database_backend();
83 let result = SelectResult::find_by_statement(builder.build(&stmt))
84 .one(&self.db_con)
85 .await?
86 .ok_or(DbError::FailedToCountCrates)?
87 .count
88 .ok_or(DbError::FailedToCountCrates)?;
89
90 Ok(result as u64)
91 }
92
93 async fn get_total_cached_crate_versions(&self) -> DbResult<u64> {
94 #[derive(Debug, PartialEq, FromQueryResult)]
95 struct SelectResult {
96 count: Option<i64>,
97 }
98
99 let stmt = Query::select()
100 .expr_as(
101 Expr::col((CratesIoMetaIden::Table, CratesIoMetaIden::Id)).count(),
102 Alias::new("count"),
103 )
104 .from(CratesIoMetaIden::Table)
105 .to_owned();
106
107 let builder = self.db_con.get_database_backend();
108 let result = SelectResult::find_by_statement(builder.build(&stmt))
109 .one(&self.db_con)
110 .await?
111 .ok_or(DbError::FailedToCountCrateVersions)?
112 .count
113 .ok_or(DbError::FailedToCountCrateVersions)?;
114
115 Ok(result as u64)
116 }
117
118 async fn get_total_cached_downloads(&self) -> DbResult<u64> {
119 #[derive(FromQueryResult)]
120 struct Model {
121 total_downloads: i64,
122 }
123
124 let total_downloads = cratesio_crate::Entity::find()
125 .select_only()
126 .column(cratesio_crate::Column::TotalDownloads)
127 .into_model::<Model>()
128 .all(&self.db_con)
129 .await?;
130
131 Ok(total_downloads
132 .iter()
133 .map(|m| m.total_downloads as u64)
134 .sum())
135 }
136
137 async fn authenticate_user(&self, name: &str, pwd: &str) -> DbResult<User> {
138 let user = self.get_user(name).await?;
139
140 if hash_pwd(pwd, &user.salt) == user.pwd {
141 Ok(user)
142 } else {
143 Err(DbError::PasswordMismatch)
144 }
145 }
146
147 async fn increase_download_counter(
148 &self,
149 crate_name: &NormalizedName,
150 crate_version: &Version,
151 ) -> DbResult<()> {
152 let krate: krate::Model = krate::Entity::find()
153 .filter(krate::Column::Name.eq(crate_name.to_string()))
154 .one(&self.db_con)
155 .await?
156 .ok_or_else(|| DbError::CrateNotFound(crate_name.to_string()))?;
157 let crate_id = krate.id;
158 let crate_total_downloads = krate.total_downloads;
159
160 let mut k: krate::ActiveModel = krate.into();
162 k.total_downloads = Set(crate_total_downloads + 1);
163 k.update(&self.db_con).await?;
164
165 crate_meta::Entity::update_many()
167 .col_expr(
168 crate_meta::Column::Downloads,
169 Expr::col(crate_meta::Column::Downloads).add(1),
170 )
171 .filter(
172 Cond::all()
173 .add(crate_meta::Column::Version.eq(crate_version.to_string()))
174 .add(crate_meta::Column::CrateFk.eq(crate_id)),
175 )
176 .exec(&self.db_con)
177 .await?;
178
179 Ok(())
180 }
181
182 async fn increase_cached_download_counter(
183 &self,
184 crate_name: &NormalizedName,
185 crate_version: &Version,
186 ) -> DbResult<()> {
187 let krate: cratesio_crate::Model = cratesio_crate::Entity::find()
188 .filter(cratesio_crate::Column::Name.eq(crate_name.to_string()))
189 .one(&self.db_con)
190 .await?
191 .ok_or_else(|| DbError::CrateNotFound(crate_name.to_string()))?;
192 let crate_id = krate.id;
193 let crate_total_downloads = krate.total_downloads;
194
195 let mut k: cratesio_crate::ActiveModel = krate.into();
197 k.total_downloads = Set(crate_total_downloads + 1);
198 k.update(&self.db_con).await?;
199
200 cratesio_meta::Entity::update_many()
202 .col_expr(
203 cratesio_meta::Column::Downloads,
204 Expr::col(cratesio_meta::Column::Downloads).add(1),
205 )
206 .filter(
207 Cond::all()
208 .add(cratesio_meta::Column::Version.eq(crate_version.to_string()))
209 .add(cratesio_meta::Column::CratesIoFk.eq(crate_id)),
210 )
211 .exec(&self.db_con)
212 .await?;
213
214 Ok(())
215 }
216
217 async fn get_last_updated_crate(&self) -> DbResult<Option<(OriginalName, Version)>> {
218 let krate = krate::Entity::find()
219 .order_by_desc(krate::Column::LastUpdated)
220 .one(&self.db_con)
221 .await?;
222
223 if let Some(krate) = krate {
224 let name = OriginalName::from_unchecked(krate.original_name);
226 let version = Version::from_unchecked_str(&krate.max_version);
228 Ok(Some((name, version)))
229 } else {
230 Ok(None)
231 }
232 }
233
234 async fn validate_session(&self, session_token: &str) -> DbResult<(String, bool)> {
235 let u = user::Entity::find()
236 .join(JoinType::InnerJoin, user::Relation::Session.def())
237 .filter(session::Column::Token.eq(session_token))
238 .one(&self.db_con)
239 .await?
240 .ok_or(DbError::SessionNotFound)?;
241
242 Ok((u.name, u.is_admin))
243 }
244
245 async fn add_session_token(&self, name: &str, session_token: &str) -> DbResult<()> {
246 let user = self.get_user(name).await?;
247 let created = Utc::now().format(DB_DATE_FORMAT).to_string();
248
249 let s = session::ActiveModel {
250 token: Set(session_token.to_owned()),
251 created: Set(created),
252 user_fk: Set(user.id as i64),
253 ..Default::default()
254 };
255
256 s.insert(&self.db_con).await?;
257 Ok(())
258 }
259
260 async fn add_crate_user(&self, crate_name: &NormalizedName, user: &str) -> DbResult<()> {
261 let user_fk = user::Entity::find()
262 .filter(user::Column::Name.eq(user))
263 .one(&self.db_con)
264 .await?
265 .map(|model| model.id)
266 .ok_or_else(|| DbError::UserNotFound(user.to_string()))?;
267
268 let crate_fk: i64 = krate::Entity::find()
269 .filter(krate::Column::Name.eq(crate_name.to_string()))
270 .one(&self.db_con)
271 .await?
272 .map(|model| model.id)
273 .ok_or_else(|| DbError::CrateNotFound(crate_name.to_string()))?;
274
275 let u = crate_user::ActiveModel {
276 user_fk: Set(user_fk),
277 crate_fk: Set(crate_fk),
278 ..Default::default()
279 };
280
281 CrateUser::insert(u).exec(&self.db_con).await?;
282 Ok(())
283 }
284
285 async fn add_crate_group(&self, crate_name: &NormalizedName, group: &str) -> DbResult<()> {
286 let group_fk = group::Entity::find()
287 .filter(group::Column::Name.eq(group))
288 .one(&self.db_con)
289 .await?
290 .map(|model| model.id)
291 .ok_or_else(|| DbError::GroupNotFound(group.to_string()))?;
292
293 let crate_fk: i64 = krate::Entity::find()
294 .filter(krate::Column::Name.eq(crate_name.to_string()))
295 .one(&self.db_con)
296 .await?
297 .map(|model| model.id)
298 .ok_or_else(|| DbError::CrateNotFound(crate_name.to_string()))?;
299
300 let u = crate_group::ActiveModel {
301 group_fk: Set(group_fk),
302 crate_fk: Set(crate_fk),
303 ..Default::default()
304 };
305
306 CrateGroup::insert(u).exec(&self.db_con).await?;
307 Ok(())
308 }
309
310 async fn add_group_user(&self, group_name: &str, user: &str) -> DbResult<()> {
311 let user_fk = user::Entity::find()
312 .filter(user::Column::Name.eq(user))
313 .one(&self.db_con)
314 .await?
315 .map(|model| model.id)
316 .ok_or_else(|| DbError::UserNotFound(user.to_string()))?;
317
318 let group_fk: i64 = group::Entity::find()
319 .filter(group::Column::Name.eq(group_name.to_string()))
320 .one(&self.db_con)
321 .await?
322 .map(|model| model.id)
323 .ok_or_else(|| DbError::GroupNotFound(group_name.to_string()))?;
324
325 let u = group_user::ActiveModel {
326 user_fk: Set(user_fk),
327 group_fk: Set(group_fk),
328 ..Default::default()
329 };
330
331 GroupUser::insert(u).exec(&self.db_con).await?;
332 Ok(())
333 }
334
335 async fn add_owner(&self, crate_name: &NormalizedName, owner: &str) -> DbResult<()> {
336 let user_fk = user::Entity::find()
337 .filter(user::Column::Name.eq(owner))
338 .one(&self.db_con)
339 .await?
340 .map(|model| model.id)
341 .ok_or_else(|| DbError::UserNotFound(owner.to_string()))?;
342
343 let crate_fk: i64 = krate::Entity::find()
344 .filter(krate::Column::Name.eq(crate_name.to_string()))
345 .one(&self.db_con)
346 .await?
347 .map(|model| model.id)
348 .ok_or_else(|| DbError::CrateNotFound(crate_name.to_string()))?;
349
350 let o = owner::ActiveModel {
351 user_fk: Set(user_fk),
352 crate_fk: Set(crate_fk),
353 ..Default::default()
354 };
355
356 Owner::insert(o).exec(&self.db_con).await?;
357 Ok(())
358 }
359
360 async fn is_download_restricted(&self, crate_name: &NormalizedName) -> DbResult<bool> {
361 let restricted_download = krate::Entity::find()
362 .filter(krate::Column::Name.eq(crate_name.to_string()))
363 .one(&self.db_con)
364 .await?
365 .map(|model| model.restricted_download);
366
367 match restricted_download {
368 Some(restricted) => Ok(restricted),
369 None => Ok(false),
370 }
371 }
372
373 async fn change_download_restricted(
374 &self,
375 crate_name: &NormalizedName,
376 restricted: bool,
377 ) -> DbResult<()> {
378 let mut krate: krate::ActiveModel = krate::Entity::find()
379 .filter(krate::Column::Name.eq(crate_name.to_string()))
380 .one(&self.db_con)
381 .await?
382 .ok_or_else(|| DbError::CrateNotFound(crate_name.to_string()))?
383 .into();
384
385 krate.restricted_download = Set(restricted);
386 krate.update(&self.db_con).await?;
387 Ok(())
388 }
389
390 async fn is_crate_user(&self, crate_name: &NormalizedName, user: &str) -> DbResult<bool> {
391 let user = crate_user::Entity::find()
392 .join(JoinType::InnerJoin, crate_user::Relation::Krate.def())
393 .join(JoinType::InnerJoin, crate_user::Relation::User.def())
394 .filter(
395 Cond::all()
396 .add(krate::Column::Name.eq(crate_name.to_string()))
397 .add(user::Column::Name.eq(user)),
398 )
399 .one(&self.db_con)
400 .await?;
401
402 Ok(user.is_some())
403 }
404
405 async fn is_crate_group(&self, crate_name: &NormalizedName, group: &str) -> DbResult<bool> {
406 let group = crate_group::Entity::find()
407 .join(JoinType::InnerJoin, crate_group::Relation::Krate.def())
408 .join(JoinType::InnerJoin, crate_group::Relation::Group.def())
409 .filter(
410 Cond::all()
411 .add(krate::Column::Name.eq(crate_name.to_string()))
412 .add(group::Column::Name.eq(group)),
413 )
414 .one(&self.db_con)
415 .await?;
416
417 Ok(group.is_some())
418 }
419
420 async fn is_crate_group_user(&self, crate_name: &NormalizedName, user: &str) -> DbResult<bool> {
421 let user = user::Entity::find()
422 .join(JoinType::InnerJoin, user::Relation::GroupUser.def())
423 .join(JoinType::InnerJoin, group_user::Relation::Group.def())
424 .join(JoinType::InnerJoin, group::Relation::CrateGroup.def())
425 .join(JoinType::InnerJoin, crate_group::Relation::Krate.def())
426 .filter(
427 Cond::all()
428 .add(krate::Column::Name.eq(crate_name.to_string()))
429 .add(user::Column::Name.eq(user)),
430 )
431 .one(&self.db_con)
432 .await?;
433 Ok(user.is_some())
434 }
435
436 async fn is_group_user(&self, group_name: &str, user: &str) -> DbResult<bool> {
437 let user = group_user::Entity::find()
438 .join(JoinType::InnerJoin, group_user::Relation::Group.def())
439 .join(JoinType::InnerJoin, group_user::Relation::User.def())
440 .filter(
441 Cond::all()
442 .add(group::Column::Name.eq(group_name.to_string()))
443 .add(user::Column::Name.eq(user)),
444 )
445 .one(&self.db_con)
446 .await?;
447
448 Ok(user.is_some())
449 }
450
451 async fn is_owner(&self, crate_name: &NormalizedName, user: &str) -> DbResult<bool> {
452 let owner = owner::Entity::find()
453 .join(JoinType::InnerJoin, owner::Relation::Krate.def())
454 .join(JoinType::InnerJoin, owner::Relation::User.def())
455 .filter(
456 Cond::all()
457 .add(krate::Column::Name.eq(crate_name.to_string()))
458 .add(user::Column::Name.eq(user)),
459 )
460 .one(&self.db_con)
461 .await?;
462
463 Ok(owner.is_some())
464 }
465
466 async fn get_crate_id(&self, crate_name: &NormalizedName) -> DbResult<Option<i64>> {
467 let id = krate::Entity::find()
468 .filter(krate::Column::Name.eq(crate_name.to_string()))
469 .one(&self.db_con)
470 .await?
471 .map(|model| model.id);
472
473 Ok(id)
474 }
475
476 async fn get_crate_owners(&self, crate_name: &NormalizedName) -> DbResult<Vec<User>> {
477 let u = user::Entity::find()
478 .join(JoinType::InnerJoin, user::Relation::Owner.def())
479 .join(JoinType::InnerJoin, owner::Relation::Krate.def())
480 .filter(Expr::col((CrateIden::Table, krate::Column::Name)).eq(crate_name.to_string()))
481 .all(&self.db_con)
482 .await?;
483
484 Ok(u.into_iter()
485 .map(|u| User {
486 id: u.id as i32,
487 name: u.name,
488 pwd: u.pwd,
489 salt: u.salt,
490 is_admin: u.is_admin,
491 is_read_only: u.is_read_only,
492 })
493 .collect())
494 }
495
496 async fn get_crate_users(&self, crate_name: &NormalizedName) -> DbResult<Vec<User>> {
497 let u = user::Entity::find()
498 .join(JoinType::InnerJoin, user::Relation::CrateUser.def())
499 .join(JoinType::InnerJoin, crate_user::Relation::Krate.def())
500 .filter(Expr::col((CrateIden::Table, krate::Column::Name)).eq(crate_name.to_string()))
501 .all(&self.db_con)
502 .await?;
503
504 Ok(u.into_iter()
505 .map(|u| User {
506 id: u.id as i32,
507 name: u.name,
508 pwd: u.pwd,
509 salt: u.salt,
510 is_admin: u.is_admin,
511 is_read_only: u.is_read_only,
512 })
513 .collect())
514 }
515
516 async fn get_crate_groups(&self, crate_name: &NormalizedName) -> DbResult<Vec<Group>> {
517 let u = group::Entity::find()
518 .join(JoinType::InnerJoin, group::Relation::CrateGroup.def())
519 .join(JoinType::InnerJoin, crate_group::Relation::Krate.def())
520 .filter(Expr::col((CrateIden::Table, krate::Column::Name)).eq(crate_name.to_string()))
521 .all(&self.db_con)
522 .await?;
523
524 Ok(u.into_iter()
525 .map(|u| Group {
526 id: u.id as i32,
527 name: u.name,
528 })
529 .collect())
530 }
531
532 async fn get_group_users(&self, group_name: &str) -> DbResult<Vec<User>> {
533 let u = user::Entity::find()
534 .join(JoinType::InnerJoin, user::Relation::GroupUser.def())
535 .join(JoinType::InnerJoin, group_user::Relation::Group.def())
536 .filter(Expr::col((GroupIden::Table, group::Column::Name)).eq(group_name.to_string()))
537 .all(&self.db_con)
538 .await?;
539
540 Ok(u.into_iter()
541 .map(|u| User {
542 id: u.id as i32,
543 name: u.name,
544 pwd: u.pwd,
545 salt: u.salt,
546 is_admin: u.is_admin,
547 is_read_only: u.is_read_only,
548 })
549 .collect())
550 }
551
552 async fn get_crate_versions(&self, crate_name: &NormalizedName) -> DbResult<Vec<Version>> {
553 let u = crate_meta::Entity::find()
554 .join(JoinType::InnerJoin, crate_meta::Relation::Krate.def())
555 .filter(Expr::col((CrateIden::Table, krate::Column::Name)).eq(crate_name.to_string()))
556 .all(&self.db_con)
557 .await?;
558
559 Ok(u.into_iter()
560 .map(|meta| Version::from_unchecked_str(&meta.version))
561 .collect())
562 }
563
564 async fn delete_session_token(&self, session_token: &str) -> DbResult<()> {
565 if let Some(s) = session::Entity::find()
566 .filter(session::Column::Token.eq(session_token))
567 .one(&self.db_con)
568 .await?
569 {
570 s.delete(&self.db_con).await?;
571 }
572
573 Ok(())
574 }
575
576 async fn delete_user(&self, user_name: &str) -> DbResult<()> {
577 let u = user::Entity::find()
578 .filter(user::Column::Name.eq(user_name))
579 .one(&self.db_con)
580 .await?
581 .ok_or_else(|| DbError::UserNotFound(user_name.to_owned()))?;
582
583 u.delete(&self.db_con).await?;
584 Ok(())
585 }
586
587 async fn delete_group(&self, group_name: &str) -> DbResult<()> {
588 let g = group::Entity::find()
589 .filter(group::Column::Name.eq(group_name))
590 .one(&self.db_con)
591 .await?
592 .ok_or_else(|| DbError::GroupNotFound(group_name.to_owned()))?;
593
594 g.delete(&self.db_con).await?;
595 Ok(())
596 }
597
598 async fn change_pwd(&self, user_name: &str, new_pwd: &str) -> DbResult<()> {
599 let salt = generate_salt();
600 let hashed = hash_pwd(new_pwd, &salt);
601
602 let mut u: user::ActiveModel = user::Entity::find()
603 .filter(user::Column::Name.eq(user_name))
604 .one(&self.db_con)
605 .await?
606 .ok_or_else(|| DbError::UserNotFound(user_name.to_owned()))?
607 .into();
608
609 u.pwd = Set(hashed.clone());
610 u.salt = Set(salt);
611
612 u.update(&self.db_con).await?;
613 Ok(())
614 }
615
616 async fn change_read_only_state(&self, user_name: &str, state: bool) -> DbResult<()> {
617 let mut u: user::ActiveModel = user::Entity::find()
618 .filter(user::Column::Name.eq(user_name))
619 .one(&self.db_con)
620 .await?
621 .ok_or_else(|| DbError::UserNotFound(user_name.to_owned()))?
622 .into();
623
624 u.is_read_only = Set(state);
625
626 u.update(&self.db_con).await?;
627 Ok(())
628 }
629
630 async fn change_admin_state(&self, user_name: &str, state: bool) -> DbResult<()> {
631 let mut u: user::ActiveModel = user::Entity::find()
632 .filter(user::Column::Name.eq(user_name))
633 .one(&self.db_con)
634 .await?
635 .ok_or_else(|| DbError::UserNotFound(user_name.to_owned()))?
636 .into();
637
638 u.is_admin = Set(state);
639
640 u.update(&self.db_con).await?;
641 Ok(())
642 }
643
644 async fn crate_version_exists(&self, crate_id: i64, version: &str) -> DbResult<bool> {
645 let cm = crate_meta::Entity::find()
646 .filter(
647 Cond::all()
648 .add(crate_meta::Column::CrateFk.eq(crate_id))
649 .add(crate_meta::Column::Version.eq(version)),
650 )
651 .one(&self.db_con)
652 .await?;
653
654 Ok(cm.is_some())
655 }
656
657 async fn get_max_version_from_id(&self, crate_id: i64) -> DbResult<Version> {
658 get_max_version_from_id(&self.db_con, crate_id).await
659 }
660
661 async fn get_max_version_from_name(&self, crate_name: &NormalizedName) -> DbResult<Version> {
662 let krate = krate::Entity::find()
663 .filter(krate::Column::Name.eq(crate_name.to_string()))
664 .one(&self.db_con)
665 .await?;
666
667 let k =
668 krate.ok_or_else(|| DbError::FailedToGetMaxVersionByName(crate_name.to_string()))?;
669 let v = Version::try_from(&k.max_version)
670 .map_err(|_| DbError::FailedToGetMaxVersionByName(crate_name.to_string()))?;
671 Ok(v)
672 }
673
674 async fn update_max_version(&self, crate_id: i64, version: &Version) -> DbResult<()> {
675 let krate = krate::Entity::find_by_id(crate_id)
676 .one(&self.db_con)
677 .await?
678 .ok_or(DbError::CrateNotFoundWithId(crate_id))?;
679
680 let mut k: krate::ActiveModel = krate.into();
681 k.max_version = Set(version.to_string());
682 k.update(&self.db_con).await?;
683
684 Ok(())
685 }
686
687 async fn add_auth_token(&self, name: &str, token: &str, user: &str) -> DbResult<()> {
688 let hashed_token = hash_token(token);
689
690 let user = user::Entity::find()
691 .filter(user::Column::Name.eq(user))
692 .one(&self.db_con)
693 .await?
694 .ok_or_else(|| DbError::UserNotFound(user.to_string()))?;
695
696 let at = auth_token::ActiveModel {
697 name: Set(name.to_owned()),
698 token: Set(hashed_token.clone()),
699 user_fk: Set(user.id),
700 ..Default::default()
701 };
702
703 at.insert(&self.db_con).await?;
704
705 Ok(())
706 }
707
708 async fn get_user_from_token(&self, token: &str) -> DbResult<User> {
709 let token = hash_token(token);
710
711 let u = user::Entity::find()
712 .join(JoinType::InnerJoin, user::Relation::AuthToken.def())
713 .filter(Expr::col((AuthTokenIden::Table, AuthTokenIden::Token)).eq(token))
714 .one(&self.db_con)
715 .await?
716 .ok_or(DbError::TokenNotFound)?;
717
718 Ok(User {
719 id: u.id as i32,
720 name: u.name,
721 pwd: u.pwd,
722 salt: u.salt,
723 is_admin: u.is_admin,
724 is_read_only: u.is_read_only,
725 })
726 }
727
728 async fn get_user(&self, name: &str) -> DbResult<User> {
729 let u = user::Entity::find()
730 .filter(user::Column::Name.eq(name))
731 .one(&self.db_con)
732 .await?
733 .ok_or_else(|| DbError::UserNotFound(name.to_owned()))?;
734
735 Ok(User {
736 id: u.id as i32,
737 name: u.name,
738 pwd: u.pwd,
739 salt: u.salt,
740 is_admin: u.is_admin,
741 is_read_only: u.is_read_only,
742 })
743 }
744
745 async fn get_group(&self, name: &str) -> DbResult<Group> {
746 let g = group::Entity::find()
747 .filter(group::Column::Name.eq(name))
748 .one(&self.db_con)
749 .await?
750 .ok_or_else(|| DbError::GroupNotFound(name.to_owned()))?;
751
752 Ok(Group {
753 id: g.id as i32,
754 name: g.name,
755 })
756 }
757
758 async fn get_auth_tokens(&self, user_name: &str) -> DbResult<Vec<AuthToken>> {
759 let at: Vec<auth_token::Model> = auth_token::Entity::find()
760 .join(JoinType::InnerJoin, auth_token::Relation::User.def())
761 .filter(user::Column::Name.eq(user_name))
762 .all(&self.db_con)
763 .await?;
764
765 Ok(at
766 .into_iter()
767 .map(|x| AuthToken::new(x.id as i32, x.name, x.token))
768 .collect())
769 }
770
771 async fn delete_auth_token(&self, id: i32) -> DbResult<()> {
772 auth_token::Entity::delete_by_id(id as i64)
773 .exec(&self.db_con)
774 .await?;
775 Ok(())
776 }
777
778 async fn delete_owner(&self, crate_name: &str, owner: &str) -> DbResult<()> {
779 let owner = owner::Entity::find()
780 .join(JoinType::InnerJoin, owner::Relation::Krate.def())
781 .join(JoinType::InnerJoin, owner::Relation::User.def())
782 .filter(
783 Cond::all()
784 .add(krate::Column::Name.eq(crate_name))
785 .add(user::Column::Name.eq(owner)),
786 )
787 .one(&self.db_con)
788 .await?
789 .ok_or_else(|| DbError::OwnerNotFound(owner.to_string()))?;
790
791 owner.delete(&self.db_con).await?;
792
793 Ok(())
794 }
795
796 async fn delete_crate_user(&self, crate_name: &str, user: &str) -> DbResult<()> {
797 let user = crate_user::Entity::find()
798 .join(JoinType::InnerJoin, crate_user::Relation::Krate.def())
799 .join(JoinType::InnerJoin, crate_user::Relation::User.def())
800 .filter(
801 Cond::all()
802 .add(krate::Column::Name.eq(crate_name))
803 .add(user::Column::Name.eq(user)),
804 )
805 .one(&self.db_con)
806 .await?
807 .ok_or_else(|| DbError::UserNotFound(user.to_string()))?;
808
809 user.delete(&self.db_con).await?;
810
811 Ok(())
812 }
813
814 async fn delete_crate_group(&self, crate_name: &NormalizedName, group: &str) -> DbResult<()> {
815 let group = crate_group::Entity::find()
816 .join(JoinType::InnerJoin, crate_group::Relation::Krate.def())
817 .join(JoinType::InnerJoin, crate_group::Relation::Group.def())
818 .filter(
819 Cond::all()
820 .add(krate::Column::Name.eq(crate_name.to_string()))
821 .add(group::Column::Name.eq(group)),
822 )
823 .one(&self.db_con)
824 .await?
825 .ok_or_else(|| DbError::GroupNotFound(group.to_string()))?;
826
827 group.delete(&self.db_con).await?;
828
829 Ok(())
830 }
831
832 async fn delete_group_user(&self, group_name: &str, user: &str) -> DbResult<()> {
833 let user = group_user::Entity::find()
834 .join(JoinType::InnerJoin, group_user::Relation::Group.def())
835 .join(JoinType::InnerJoin, group_user::Relation::User.def())
836 .filter(
837 Cond::all()
838 .add(group::Column::Name.eq(group_name))
839 .add(user::Column::Name.eq(user)),
840 )
841 .one(&self.db_con)
842 .await?
843 .ok_or_else(|| DbError::UserNotFound(user.to_string()))?;
844
845 user.delete(&self.db_con).await?;
846
847 Ok(())
848 }
849
850 async fn add_user(
851 &self,
852 name: &str,
853 pwd: &str,
854 salt: &str,
855 is_admin: bool,
856 is_read_only: bool,
857 ) -> DbResult<()> {
858 let hashed_pwd = hash_pwd(pwd, salt);
859
860 let u = user::ActiveModel {
861 name: Set(name.to_owned()),
862 pwd: Set(hashed_pwd),
863 salt: Set(salt.to_owned()),
864 is_admin: Set(is_admin),
865 is_read_only: Set(is_read_only),
866 ..Default::default()
867 };
868
869 u.insert(&self.db_con).await?;
870 Ok(())
871 }
872
873 async fn add_group(&self, name: &str) -> DbResult<()> {
874 let g = group::ActiveModel {
875 name: Set(name.to_owned()),
876 ..Default::default()
877 };
878
879 g.insert(&self.db_con).await?;
880 Ok(())
881 }
882
883 async fn get_users(&self) -> DbResult<Vec<User>> {
884 let users = user::Entity::find()
885 .order_by_asc(user::Column::Name)
886 .all(&self.db_con)
887 .await?;
888
889 Ok(users
890 .into_iter()
891 .map(|u| User {
892 id: u.id as i32,
893 name: u.name,
894 pwd: u.pwd,
895 salt: u.salt,
896 is_admin: u.is_admin,
897 is_read_only: u.is_read_only,
898 })
899 .collect())
900 }
901
902 async fn get_groups(&self) -> DbResult<Vec<Group>> {
903 let groups = group::Entity::find()
904 .order_by_asc(group::Column::Name)
905 .all(&self.db_con)
906 .await?;
907
908 Ok(groups
909 .into_iter()
910 .map(|g| Group {
911 id: g.id as i32,
912 name: g.name,
913 })
914 .collect())
915 }
916
917 async fn get_total_unique_crates(&self) -> DbResult<u32> {
918 #[derive(Debug, PartialEq, FromQueryResult)]
919 struct SelectResult {
920 count: Option<i64>,
921 }
922
923 let stmt = Query::select()
924 .expr_as(
925 Expr::col((CrateIden::Table, CrateIden::Id)).count(),
926 Alias::new("count"),
927 )
928 .from(CrateIden::Table)
929 .to_owned();
930
931 let builder = self.db_con.get_database_backend();
932 let result = SelectResult::find_by_statement(builder.build(&stmt))
933 .one(&self.db_con)
934 .await?
935 .ok_or(DbError::FailedToCountCrates)?
936 .count
937 .ok_or(DbError::FailedToCountCrates)?;
938
939 Ok(result as u32)
940 }
941
942 async fn get_total_crate_versions(&self) -> DbResult<u32> {
943 #[derive(Debug, PartialEq, FromQueryResult)]
944 struct SelectResult {
945 count: Option<i64>,
946 }
947
948 let stmt = Query::select()
949 .expr_as(
950 Expr::col((CrateMetaIden::Table, CrateMetaIden::Id)).count(),
951 Alias::new("count"),
952 )
953 .from(CrateMetaIden::Table)
954 .to_owned();
955
956 let builder = self.db_con.get_database_backend();
957 let result = SelectResult::find_by_statement(builder.build(&stmt))
958 .one(&self.db_con)
959 .await?
960 .ok_or(DbError::FailedToCountCrateVersions)?
961 .count
962 .ok_or(DbError::FailedToCountCrateVersions)?;
963
964 Ok(result as u32)
965 }
966
967 async fn get_total_downloads(&self) -> DbResult<u64> {
968 #[derive(FromQueryResult)]
969 struct Model {
970 total_downloads: i64,
971 }
972
973 let total_downloads = krate::Entity::find()
974 .select_only()
975 .column(krate::Column::TotalDownloads)
976 .into_model::<Model>()
977 .all(&self.db_con)
978 .await?;
979
980 Ok(total_downloads
981 .iter()
982 .map(|m| m.total_downloads as u64)
983 .sum())
984 }
985
986 async fn get_top_crates_downloads(&self, top: u32) -> DbResult<Vec<(String, u64)>> {
987 #[derive(Debug, PartialEq, FromQueryResult)]
988 struct SelectResult {
989 original_name: String,
990 total_downloads: i64,
991 }
992
993 let stmt = Query::select()
994 .columns(vec![CrateIden::OriginalName, CrateIden::TotalDownloads])
995 .from(CrateIden::Table)
996 .order_by(CrateIden::TotalDownloads, Order::Desc)
997 .limit(top as u64)
998 .to_owned();
999
1000 let builder = self.db_con.get_database_backend();
1001 let result = SelectResult::find_by_statement(builder.build(&stmt))
1002 .all(&self.db_con)
1003 .await?;
1004
1005 Ok(result
1006 .iter()
1007 .map(|x| (x.original_name.clone(), x.total_downloads as u64))
1008 .collect())
1009 }
1010
1011 async fn get_crate_summaries(&self) -> DbResult<Vec<CrateSummary>> {
1012 let krates = krate::Entity::find()
1013 .order_by(krate::Column::Name, Order::Asc)
1014 .all(&self.db_con)
1015 .await?;
1016
1017 let krates = krates
1018 .iter()
1019 .map(|c| CrateSummary {
1020 name: c.name.clone(),
1021 max_version: c.max_version.clone(),
1022 last_updated: c.last_updated.clone(),
1023 total_downloads: c.total_downloads,
1024 })
1025 .collect();
1026
1027 Ok(krates)
1028 }
1029
1030 async fn add_doc_queue(
1031 &self,
1032 krate: &NormalizedName,
1033 version: &Version,
1034 path: &Path,
1035 ) -> DbResult<()> {
1036 let s = doc_queue::ActiveModel {
1037 krate: Set(krate.to_string()),
1038 version: Set(version.to_string()),
1039 path: Set(path.to_string_lossy().to_string()),
1040 ..Default::default()
1041 };
1042
1043 s.insert(&self.db_con).await?;
1044 Ok(())
1045 }
1046
1047 async fn delete_doc_queue(&self, id: i64) -> DbResult<()> {
1048 DocQueue::delete_by_id(id).exec(&self.db_con).await?;
1049 Ok(())
1050 }
1051
1052 async fn get_doc_queue(&self) -> DbResult<Vec<DocQueueEntry>> {
1053 let entities = DocQueue::find().all(&self.db_con).await?;
1054
1055 Ok(entities.into_iter().map(DocQueueEntry::from).collect())
1056 }
1057
1058 async fn delete_crate(&self, krate: &NormalizedName, version: &Version) -> DbResult<()> {
1059 let txn = self.db_con.begin().await?;
1060
1061 let crate_meta_version = crate_meta::Entity::find()
1063 .join(JoinType::InnerJoin, crate_meta::Relation::Krate.def())
1064 .filter(krate::Column::Name.eq(krate.to_string()))
1065 .filter(crate_meta::Column::Version.eq(version.to_string()))
1066 .one(&txn)
1067 .await?
1068 .ok_or_else(|| DbError::CrateMetaNotFound(krate.to_string(), version.to_string()))?;
1069 let crate_id = crate_meta_version.crate_fk;
1070 let current_max_version = get_max_version_from_id(&txn, crate_id).await?;
1071 crate_meta_version.delete(&txn).await?;
1072
1073 let crate_index_version = crate_index::Entity::find()
1075 .join(JoinType::InnerJoin, crate_index::Relation::Krate.def())
1076 .filter(krate::Column::Name.eq(krate.to_string()))
1077 .filter(crate_index::Column::Vers.eq(version.to_string()))
1078 .one(&txn)
1079 .await?
1080 .ok_or_else(|| DbError::CrateIndexNotFound(krate.to_string(), version.to_string()))?;
1081 crate_index_version.delete(&txn).await?;
1082
1083 let crate_meta_rows = crate_meta::Entity::find()
1086 .join(JoinType::InnerJoin, crate_meta::Relation::Krate.def())
1087 .filter(krate::Column::Name.eq(krate.to_string()))
1088 .all(&txn)
1089 .await?;
1090
1091 if crate_meta_rows.is_empty() {
1092 krate::Entity::delete_many()
1093 .filter(krate::Column::Name.eq(krate.to_string()))
1094 .exec(&txn)
1095 .await?;
1096 } else {
1097 let c = krate::Entity::find_by_id(crate_id)
1098 .one(&txn)
1099 .await?
1100 .ok_or(DbError::CrateNotFoundWithId(crate_id))?;
1101 let mut c: krate::ActiveModel = c.into();
1102
1103 if version == ¤t_max_version {
1105 let new_max_version = crate_meta_rows
1106 .iter()
1107 .map(|cm| Version::from_unchecked_str(&cm.version))
1108 .max()
1109 .unwrap(); c.max_version = Set(new_max_version.to_string());
1111 }
1112 let etag = compute_etag(&txn, krate, crate_id).await?;
1114 c.e_tag = Set(etag);
1115 c.update(&txn).await?;
1116 }
1117
1118 txn.commit().await?;
1119
1120 Ok(())
1121 }
1122
1123 async fn get_crate_meta_list(&self, crate_name: &NormalizedName) -> DbResult<Vec<CrateMeta>> {
1124 let crate_meta = crate_meta::Entity::find()
1125 .join(JoinType::InnerJoin, crate_meta::Relation::Krate.def())
1126 .filter(krate::Column::Name.eq(crate_name.to_string()))
1127 .all(&self.db_con)
1128 .await?;
1129
1130 let crate_meta = crate_meta
1131 .into_iter()
1132 .map(|cm| CrateMeta {
1133 name: crate_name.to_string(),
1134 id: cm.id,
1135 version: cm.version,
1136 created: cm.created,
1137 downloads: cm.downloads,
1138 crate_fk: cm.crate_fk,
1139 })
1140 .collect();
1141
1142 Ok(crate_meta)
1143 }
1144
1145 async fn update_last_updated(&self, id: i64, last_updated: &DateTime<Utc>) -> DbResult<()> {
1146 let krate = krate::Entity::find_by_id(id)
1147 .one(&self.db_con)
1148 .await?
1149 .ok_or(DbError::CrateNotFoundWithId(id))?;
1150
1151 let date = last_updated.format(DB_DATE_FORMAT).to_string();
1152
1153 let mut krate: krate::ActiveModel = krate.into();
1154 krate.last_updated = Set(date);
1155 krate.update(&self.db_con).await?;
1156
1157 Ok(())
1158 }
1159
1160 async fn search_in_crate_name(
1161 &self,
1162 contains: &str,
1163 cache: bool,
1164 ) -> DbResult<Vec<CrateOverview>> {
1165 let mut stmt_kellnr = Query::select()
1166 .expr_as(Expr::col(CrateIden::OriginalName), Alias::new("name"))
1167 .expr_as(Expr::col(CrateIden::MaxVersion), Alias::new("version"))
1168 .expr_as(Expr::col(CrateIden::LastUpdated), Alias::new("date"))
1169 .expr_as(
1170 Expr::col(CrateIden::TotalDownloads),
1171 Alias::new("total_downloads"),
1172 )
1173 .expr_as(Expr::col(CrateIden::Description), Alias::new("description"))
1174 .expr_as(
1175 Expr::col(CrateMetaIden::Documentation),
1176 Alias::new("documentation"),
1177 )
1178 .expr_as(Expr::cust("false"), Alias::new("is_cache"))
1179 .from(CrateMetaIden::Table)
1180 .inner_join(
1181 CrateIden::Table,
1182 Expr::col((CrateMetaIden::Table, CrateMetaIden::CrateFk))
1183 .equals((CrateIden::Table, CrateIden::Id)),
1184 )
1185 .and_where(Expr::col((CrateIden::Table, CrateIden::Name)).like(format!("%{contains}%")))
1186 .and_where(
1187 Expr::col((CrateMetaIden::Table, CrateMetaIden::Version))
1188 .equals((CrateIden::Table, CrateIden::MaxVersion)),
1189 )
1190 .to_owned();
1191
1192 let stmt = if !cache {
1193 stmt_kellnr
1194 .order_by(CrateIden::OriginalName, Order::Asc)
1195 .to_owned()
1196 } else {
1197 stmt_kellnr
1198 .union(
1199 UnionType::All,
1200 Query::select()
1201 .expr_as(Expr::col(CratesIoIden::OriginalName), Alias::new("name"))
1202 .expr_as(Expr::col(CratesIoIden::MaxVersion), Alias::new("version"))
1203 .expr_as(Expr::col(CratesIoIden::LastModified), Alias::new("date"))
1204 .expr_as(
1205 Expr::col(CratesIoIden::TotalDownloads),
1206 Alias::new("total_downloads"),
1207 )
1208 .expr_as(
1209 Expr::col(CratesIoIden::Description),
1210 Alias::new("description"),
1211 )
1212 .expr_as(
1213 Expr::col(CratesIoMetaIden::Documentation),
1214 Alias::new("documentation"),
1215 )
1216 .expr_as(Expr::cust("true"), Alias::new("is_cache"))
1217 .from(CratesIoMetaIden::Table)
1218 .inner_join(
1219 CratesIoIden::Table,
1220 Expr::col((CratesIoMetaIden::Table, CratesIoMetaIden::CratesIoFk))
1221 .equals((CratesIoIden::Table, CratesIoIden::Id)),
1222 )
1223 .and_where(
1224 Expr::col((CratesIoMetaIden::Table, CratesIoMetaIden::Version))
1225 .equals((CratesIoIden::Table, CratesIoIden::MaxVersion)),
1226 )
1227 .and_where(
1228 Expr::col((CratesIoIden::Table, CrateIden::OriginalName))
1229 .like(format!("%{contains}%")),
1230 )
1231 .to_owned(),
1232 )
1233 .order_by(Alias::new("name"), Order::Asc)
1234 .to_owned()
1235 };
1236 let builder = self.db_con.get_database_backend();
1237 let result = CrateOverview::find_by_statement(builder.build(&stmt))
1238 .all(&self.db_con)
1239 .await?;
1240
1241 Ok(result)
1242 }
1243
1244 async fn get_crate_overview_list(
1245 &self,
1246 limit: u64,
1247 offset: u64,
1248 cache: bool,
1249 ) -> DbResult<Vec<CrateOverview>> {
1250 let mut stmt_kellnr = Query::select()
1251 .expr_as(Expr::col(CrateIden::OriginalName), Alias::new("name"))
1252 .expr_as(Expr::col(CrateIden::MaxVersion), Alias::new("version"))
1253 .expr_as(Expr::col(CrateIden::LastUpdated), Alias::new("date"))
1254 .expr_as(
1255 Expr::col(CrateIden::TotalDownloads),
1256 Alias::new("total_downloads"),
1257 )
1258 .expr_as(Expr::col(CrateIden::Description), Alias::new("description"))
1259 .expr_as(
1260 Expr::col(CrateMetaIden::Documentation),
1261 Alias::new("documentation"),
1262 )
1263 .expr_as(Expr::cust("false"), Alias::new("is_cache"))
1264 .from(CrateMetaIden::Table)
1265 .inner_join(
1266 CrateIden::Table,
1267 Expr::col((CrateMetaIden::Table, CrateMetaIden::CrateFk))
1268 .equals((CrateIden::Table, CrateIden::Id)),
1269 )
1270 .and_where(
1271 Expr::col((CrateMetaIden::Table, CrateMetaIden::Version))
1272 .equals((CrateIden::Table, CrateIden::MaxVersion)),
1273 )
1274 .to_owned();
1275
1276 let stmt = if !cache {
1277 stmt_kellnr
1278 .order_by(CrateIden::OriginalName, Order::Asc)
1279 .limit(limit)
1280 .offset(offset)
1281 .to_owned()
1282 } else {
1283 stmt_kellnr
1284 .union(
1285 UnionType::All,
1286 Query::select()
1287 .expr_as(Expr::col(CratesIoIden::OriginalName), Alias::new("name"))
1288 .expr_as(Expr::col(CratesIoIden::MaxVersion), Alias::new("version"))
1289 .expr_as(Expr::col(CratesIoIden::LastModified), Alias::new("date"))
1290 .expr_as(
1291 Expr::col(CratesIoIden::TotalDownloads),
1292 Alias::new("total_downloads"),
1293 )
1294 .expr_as(
1295 Expr::col(CratesIoIden::Description),
1296 Alias::new("description"),
1297 )
1298 .expr_as(
1299 Expr::col(CratesIoMetaIden::Documentation),
1300 Alias::new("documentation"),
1301 )
1302 .expr_as(Expr::cust("true"), Alias::new("is_cache"))
1303 .from(CratesIoMetaIden::Table)
1304 .inner_join(
1305 CratesIoIden::Table,
1306 Expr::col((CratesIoMetaIden::Table, CratesIoMetaIden::CratesIoFk))
1307 .equals((CratesIoIden::Table, CratesIoIden::Id)),
1308 )
1309 .and_where(
1310 Expr::col((CratesIoMetaIden::Table, CratesIoMetaIden::Version))
1311 .equals((CratesIoIden::Table, CratesIoIden::MaxVersion)),
1312 )
1313 .to_owned(),
1314 )
1315 .order_by(Alias::new("name"), Order::Asc)
1316 .limit(limit)
1317 .offset(offset)
1318 .to_owned()
1319 };
1320
1321 let builder = self.db_con.get_database_backend();
1322 let result = CrateOverview::find_by_statement(builder.build(&stmt))
1323 .all(&self.db_con)
1324 .await?;
1325
1326 Ok(result)
1327 }
1328
1329 async fn get_crate_data(&self, crate_name: &NormalizedName) -> DbResult<CrateData> {
1330 let krate = krate::Entity::find()
1331 .filter(krate::Column::Name.eq(crate_name.to_string()))
1332 .one(&self.db_con)
1333 .await?
1334 .ok_or_else(|| DbError::CrateNotFound(crate_name.to_string()))?;
1335
1336 let owners: Vec<String> = krate
1337 .find_related(owner::Entity)
1338 .find_also_related(user::Entity)
1339 .all(&self.db_con)
1340 .await?
1341 .into_iter()
1342 .filter_map(|(_, v)| v.map(|v| v.name))
1343 .collect();
1344 let categories: Vec<String> = krate
1345 .find_related(crate_category_to_crate::Entity)
1346 .find_also_related(crate_category::Entity)
1347 .all(&self.db_con)
1348 .await?
1349 .into_iter()
1350 .filter_map(|(_, v)| v.map(|v| v.category))
1351 .collect();
1352 let keywords: Vec<String> = krate
1353 .find_related(crate_keyword_to_crate::Entity)
1354 .find_also_related(crate_keyword::Entity)
1355 .all(&self.db_con)
1356 .await?
1357 .into_iter()
1358 .filter_map(|(_, v)| v.map(|v| v.keyword))
1359 .collect();
1360 let authors: Vec<String> = krate
1361 .find_related(crate_author_to_crate::Entity)
1362 .find_also_related(crate_author::Entity)
1363 .all(&self.db_con)
1364 .await?
1365 .into_iter()
1366 .filter_map(|(_, v)| v.map(|v| v.author))
1367 .collect();
1368 let crate_metas = krate
1369 .find_related(crate_meta::Entity)
1370 .all(&self.db_con)
1371 .await?;
1372 let crate_indices = krate
1373 .find_related(crate_index::Entity)
1374 .all(&self.db_con)
1375 .await?;
1376
1377 let mut versions = Vec::new();
1378 for cm in crate_metas {
1379 let ci = crate_indices
1380 .iter()
1381 .find(|ci| ci.vers == cm.version)
1382 .ok_or(DbError::CrateIndexNotFound(
1383 krate.name.clone(),
1384 cm.version.clone(),
1385 ))?;
1386 let dependencies: Vec<CrateRegistryDep> = match ci.deps.clone() {
1387 Some(deps) => {
1388 let ix = serde_json::from_value::<Vec<IndexDep>>(deps)
1389 .map_err(|e| DbError::FailedToConvertToJson(e.to_string()))?;
1390
1391 let mut ft = Vec::new();
1392 for dep in ix {
1393 ft.push(CrateRegistryDep::from_index(
1394 get_desc_for_crate_dep(
1395 &self.db_con,
1396 &dep.name,
1397 dep.registry.as_deref(),
1398 )
1399 .await?,
1400 dep,
1401 ));
1402 }
1403
1404 ft
1405 }
1406 None => Vec::default(),
1407 };
1408 let features: BTreeMap<String, Vec<String>> = match ci.features.clone() {
1409 Some(features) => serde_json::from_value::<BTreeMap<String, Vec<String>>>(features)
1410 .map_err(|e| DbError::FailedToConvertToJson(e.to_string()))?,
1411 None => BTreeMap::default(),
1412 };
1413
1414 versions.push(CrateVersionData {
1415 version: cm.version,
1416 created: cm.created,
1417 downloads: cm.downloads,
1418 readme: cm.readme,
1419 license: cm.license,
1420 license_file: cm.license_file,
1421 documentation: cm.documentation,
1422 dependencies,
1423 checksum: ci.cksum.clone(),
1424 features,
1425 yanked: ci.yanked,
1426 links: ci.links.clone(),
1427 v: ci.v,
1428 });
1429 }
1430 versions.sort_by(|a, b| {
1431 Version::from_unchecked_str(&b.version).cmp(&Version::from_unchecked_str(&a.version))
1432 });
1433
1434 let crate_data = CrateData {
1435 name: krate.original_name,
1436 owners,
1437 max_version: krate.max_version,
1438 total_downloads: krate.total_downloads,
1439 last_updated: krate.last_updated,
1440 homepage: krate.homepage,
1441 description: krate.description,
1442 repository: krate.repository,
1443 categories,
1444 keywords,
1445 authors,
1446 versions,
1447 };
1448
1449 Ok(crate_data)
1450 }
1451
1452 async fn add_empty_crate(&self, name: &str, created: &DateTime<Utc>) -> DbResult<i64> {
1453 let created = created.format(DB_DATE_FORMAT).to_string();
1454 let normalized_name = NormalizedName::from(
1455 OriginalName::try_from(name)
1456 .map_err(|_| DbError::InvalidCrateName(name.to_string()))?,
1457 );
1458 let krate = krate::ActiveModel {
1459 id: ActiveValue::default(),
1460 name: Set(normalized_name.to_string()),
1461 original_name: Set(name.to_string()),
1462 max_version: Set("0.0.0".to_string()),
1463 last_updated: Set(created.clone()),
1464 total_downloads: Set(0),
1465 homepage: Set(None),
1466 description: Set(None),
1467 repository: Set(None),
1468 e_tag: Set(String::new()), restricted_download: Set(false),
1470 };
1471 Ok(krate.insert(&self.db_con).await?.id)
1472 }
1473
1474 async fn add_crate(
1475 &self,
1476 pub_metadata: &PublishMetadata,
1477 cksum: &str,
1478 created: &DateTime<Utc>,
1479 owner: &str,
1480 ) -> DbResult<i64> {
1481 let created = created.format(DB_DATE_FORMAT).to_string();
1482 let normalized_name = NormalizedName::from(
1483 OriginalName::try_from(&pub_metadata.name)
1484 .map_err(|_| DbError::InvalidCrateName(pub_metadata.name.clone()))?,
1485 );
1486
1487 let existing = krate::Entity::find()
1488 .filter(krate::Column::Name.eq(pub_metadata.name.clone()))
1489 .one(&self.db_con)
1490 .await?;
1491
1492 let txn = self.db_con.begin().await?;
1493
1494 let crate_id = if let Some(krate) = existing {
1495 let krate_id = krate.id;
1496 let current_max_version = Version::try_from(&krate.max_version)
1497 .map_err(|_| DbError::InvalidVersion(krate.max_version.clone()))?;
1498 let max_version = current_max_version.max(
1499 Version::try_from(&pub_metadata.vers)
1500 .map_err(|_| DbError::InvalidVersion(pub_metadata.vers.clone()))?,
1501 );
1502
1503 let mut krate: krate::ActiveModel = krate.into();
1504 krate.last_updated = Set(created.clone());
1505 krate.max_version = Set(max_version.to_string());
1506 krate.homepage = Set(pub_metadata.homepage.clone());
1507 krate.description = Set(pub_metadata.description.clone());
1508 krate.repository = Set(pub_metadata.repository.clone());
1509 krate.e_tag = Set(String::new()); krate.update(&txn).await?;
1511 krate_id
1512 } else {
1513 let krate = krate::ActiveModel {
1514 id: ActiveValue::default(),
1515 name: Set(normalized_name.to_string()),
1516 original_name: Set(pub_metadata.name.clone()),
1517 max_version: Set(pub_metadata.vers.clone()),
1518 last_updated: Set(created.clone()),
1519 total_downloads: Set(0),
1520 homepage: Set(pub_metadata.homepage.clone()),
1521 description: Set(pub_metadata.description.clone()),
1522 repository: Set(pub_metadata.repository.clone()),
1523 e_tag: Set(String::new()), restricted_download: Set(false),
1525 };
1526 let krate = krate.insert(&txn).await?;
1527 krate.id
1528 };
1529
1530 add_owner_if_not_exists(&txn, owner, crate_id).await?;
1531 add_crate_metadata(&txn, pub_metadata, &created, crate_id).await?;
1532 add_crate_index(&txn, pub_metadata, cksum, crate_id).await?;
1533 update_etag(&txn, &pub_metadata.name, crate_id).await?;
1534 update_crate_categories(&txn, pub_metadata, crate_id).await?;
1535 update_crate_keywords(&txn, pub_metadata, crate_id).await?;
1536 update_crate_authors(&txn, pub_metadata, crate_id).await?;
1537
1538 txn.commit().await?;
1539 Ok(crate_id)
1540 }
1541
1542 async fn update_docs_link(
1543 &self,
1544 crate_name: &NormalizedName,
1545 version: &Version,
1546 docs_link: &str,
1547 ) -> DbResult<()> {
1548 let (cm, _c) = crate_meta::Entity::find()
1549 .find_also_related(krate::Entity)
1550 .filter(
1551 Cond::all()
1552 .add(krate::Column::Name.eq(crate_name.to_string()))
1553 .add(crate_meta::Column::Version.eq(version.to_string())),
1554 )
1555 .one(&self.db_con)
1556 .await?
1557 .ok_or(DbError::CrateNotFound(crate_name.to_string()))?;
1558
1559 let mut cm: crate_meta::ActiveModel = cm.into();
1560 cm.documentation = Set(Some(docs_link.to_string()));
1561 cm.update(&self.db_con).await?;
1562 Ok(())
1563 }
1564
1565 async fn add_crate_metadata(
1566 &self,
1567 pub_metadata: &PublishMetadata,
1568 created: &str,
1569 crate_id: i64,
1570 ) -> DbResult<()> {
1571 add_crate_metadata(&self.db_con, pub_metadata, created, crate_id).await
1572 }
1573
1574 async fn get_prefetch_data(&self, crate_name: &str) -> DbResult<Prefetch> {
1575 let krate = krate::Entity::find()
1576 .filter(krate::Column::Name.eq(crate_name))
1577 .find_with_related(crate_index::Entity)
1578 .all(&self.db_con)
1579 .await?;
1580
1581 if krate.len() != 1 {
1583 return Err(DbError::CrateNotFound(crate_name.to_string()));
1584 }
1585
1586 let (krate, crate_indices) = krate[0].to_owned();
1587 let index_metadata = crate_index_model_to_index_metadata(crate_name, crate_indices)?;
1588 let data = index_metadata_to_bytes(&index_metadata)?;
1589
1590 Ok(Prefetch {
1591 data,
1592 etag: krate.e_tag.clone(),
1593 last_modified: krate.last_updated,
1594 })
1595 }
1596
1597 async fn is_cratesio_cache_up_to_date(
1598 &self,
1599 crate_name: &NormalizedName,
1600 etag: Option<String>,
1601 last_modified: Option<String>,
1602 ) -> DbResult<PrefetchState> {
1603 let Some(krate) = cratesio_crate::Entity::find()
1604 .filter(cratesio_crate::Column::Name.eq(crate_name.to_string()))
1605 .one(&self.db_con)
1606 .await?
1607 else {
1608 return Ok(PrefetchState::NotFound);
1609 };
1610
1611 let needs_update = match (etag, last_modified) {
1612 (Some(etag), Some(last_modified)) => {
1613 krate.e_tag != etag || krate.last_modified != last_modified
1614 }
1615 (Some(etag), None) => krate.e_tag != etag,
1616 (None, Some(last_modified)) => krate.last_modified != last_modified,
1617 (None, None) => true,
1618 };
1619
1620 if !needs_update {
1621 Ok(PrefetchState::UpToDate)
1622 } else {
1623 let crate_indices = krate
1624 .find_related(cratesio_index::Entity)
1625 .all(&self.db_con)
1626 .await?;
1627 let index_metadata = cratesio_index_model_to_index_metadata(crate_name, crate_indices)?;
1628 let data = index_metadata_to_bytes(&index_metadata)?;
1629
1630 Ok(PrefetchState::NeedsUpdate(Prefetch {
1631 data,
1632 etag: krate.e_tag.clone(),
1633 last_modified: krate.last_modified,
1634 }))
1635 }
1636 }
1637
1638 async fn add_cratesio_prefetch_data(
1639 &self,
1640 crate_name: &OriginalName,
1641 etag: &str,
1642 last_modified: &str,
1643 description: Option<String>,
1644 indices: &[IndexMetadata],
1645 ) -> DbResult<Prefetch> {
1646 let normalized_name = crate_name.to_normalized();
1647
1648 let max_version = indices
1649 .iter()
1650 .map(|i| Version::from_unchecked_str(&i.vers))
1651 .max()
1652 .ok_or(DbError::FailedToGetMaxVersionByName(crate_name.to_string()))?;
1653
1654 let krate = cratesio_crate::Entity::find()
1655 .filter(cratesio_crate::Column::Name.eq(normalized_name.to_string()))
1656 .one(&self.db_con)
1657 .await?;
1658
1659 let krate = if let Some(krate) = krate {
1660 let mut krate: cratesio_crate::ActiveModel = krate.into();
1661 krate.e_tag = Set(etag.to_string());
1662 krate.last_modified = Set(last_modified.to_string());
1663 krate.max_version = Set(max_version.to_string());
1664 krate.update(&self.db_con).await?
1665 } else {
1666 let krate = cratesio_crate::ActiveModel {
1667 id: ActiveValue::default(),
1668 name: Set(normalized_name.to_string()),
1669 original_name: Set(crate_name.to_string()),
1670 description: Set(description),
1671 e_tag: Set(etag.to_string()),
1672 last_modified: Set(last_modified.to_string()),
1673 total_downloads: Set(0),
1674 max_version: Set(max_version.to_string()),
1675 };
1676 krate.insert(&self.db_con).await?
1677 };
1678
1679 let current_indices = cratesio_index::Entity::find()
1680 .filter(cratesio_index::Column::CratesIoFk.eq(krate.id))
1681 .all(&self.db_con)
1682 .await?;
1683
1684 for index in indices {
1685 if let Some(current_index) = current_indices.iter().find(|ci| index.vers == ci.vers) {
1687 if index.yanked != current_index.yanked {
1688 let mut ci: cratesio_index::ActiveModel = current_index.to_owned().into();
1689 ci.yanked = Set(index.yanked);
1690 ci.update(&self.db_con).await?;
1691 }
1692 } else {
1693 let deps = if index.deps.is_empty() {
1694 None
1695 } else {
1696 let deps = serde_json::to_value(&index.deps)
1697 .map_err(|e| DbError::FailedToConvertToJson(e.to_string()))?;
1698 Some(deps)
1699 };
1700
1701 let features = serde_json::to_value(&index.features)
1702 .map_err(|e| DbError::FailedToConvertToJson(e.to_string()))?;
1703
1704 let features2 = serde_json::to_value(&index.features2)
1705 .map_err(|e| DbError::FailedToConvertToJson(e.to_string()))?;
1706
1707 let new_index = cratesio_index::ActiveModel {
1708 id: ActiveValue::default(),
1709 name: Set(index.name.clone()),
1710 vers: Set(index.vers.clone()),
1711 deps: Set(deps),
1712 cksum: Set(index.cksum.clone()),
1713 features: Set(Some(features)),
1714 features2: Set(Some(features2)),
1715 yanked: Set(index.yanked),
1716 links: Set(index.links.clone()),
1717 pubtime: Set(index.pubtime.map(|dt| dt.naive_utc())),
1718 v: Set(index.v.unwrap_or(1) as i32),
1719 crates_io_fk: Set(krate.id),
1720 };
1721
1722 new_index.insert(&self.db_con).await?;
1723
1724 let meta = cratesio_meta::ActiveModel {
1726 id: ActiveValue::default(),
1727 version: Set(index.vers.clone()),
1728 downloads: Set(0),
1729 crates_io_fk: Set(krate.id),
1730 documentation: Set(Some(format!(
1731 "https://docs.rs/{normalized_name}/{}",
1732 index.vers,
1733 ))),
1734 };
1735
1736 meta.insert(&self.db_con).await?;
1737 }
1738 }
1739
1740 Ok(Prefetch {
1741 data: index_metadata_to_bytes(indices)?,
1742 etag: etag.to_string(),
1743 last_modified: last_modified.to_string(),
1744 })
1745 }
1746
1747 async fn get_cratesio_index_update_list(&self) -> DbResult<Vec<CratesioPrefetchMsg>> {
1748 let crates = cratesio_crate::Entity::find().all(&self.db_con).await?;
1749 let msgs = crates
1750 .into_iter()
1751 .map(|krate| {
1752 CratesioPrefetchMsg::Update(UpdateData {
1753 name: OriginalName::from_unchecked(krate.original_name),
1754 etag: Some(krate.e_tag),
1755 last_modified: Some(krate.last_modified),
1756 })
1757 })
1758 .collect();
1759 Ok(msgs)
1760 }
1761
1762 async fn unyank_crate(&self, crate_name: &NormalizedName, version: &Version) -> DbResult<()> {
1763 let ci = crate_index::Entity::find()
1764 .filter(crate_index::Column::Name.eq(crate_name.to_string()))
1765 .filter(crate_index::Column::Vers.eq(version.to_string()))
1766 .one(&self.db_con)
1767 .await?;
1768
1769 let mut ci: crate_index::ActiveModel = ci
1770 .ok_or(DbError::CrateIndexNotFound(
1771 crate_name.to_string(),
1772 version.to_string(),
1773 ))?
1774 .into();
1775
1776 ci.yanked = Set(false);
1777 ci.save(&self.db_con).await?;
1778
1779 Ok(())
1780 }
1781
1782 async fn yank_crate(&self, crate_name: &NormalizedName, version: &Version) -> DbResult<()> {
1783 let ci = crate_index::Entity::find()
1784 .filter(crate_index::Column::Name.eq(crate_name.to_string()))
1785 .filter(crate_index::Column::Vers.eq(version.to_string()))
1786 .one(&self.db_con)
1787 .await?;
1788
1789 let mut ci: crate_index::ActiveModel = ci
1790 .ok_or(DbError::CrateIndexNotFound(
1791 crate_name.to_string(),
1792 version.to_string(),
1793 ))?
1794 .into();
1795
1796 ci.yanked = Set(true);
1797 ci.save(&self.db_con).await?;
1798
1799 Ok(())
1800 }
1801
1802 async fn register_webhook(&self, webhook: Webhook) -> DbResult<String> {
1803 let w = webhook::ActiveModel {
1804 event: Set(Into::<&str>::into(webhook.event).to_string()),
1805 callback_url: Set(webhook.callback_url),
1806 name: Set(webhook.name),
1807 ..Default::default()
1808 };
1809
1810 let w: webhook::Model = w.insert(&self.db_con).await?;
1811 Ok(w.id.to_string())
1812 }
1813 async fn delete_webhook(&self, id: &str) -> DbResult<()> {
1814 let w = webhook::Entity::find()
1815 .filter(webhook::Column::Id.eq(
1816 TryInto::<Uuid>::try_into(id).map_err(|_| DbError::InvalidId(id.to_string()))?,
1817 ))
1818 .one(&self.db_con)
1819 .await?
1820 .ok_or(DbError::WebhookNotFound)?;
1821
1822 w.delete(&self.db_con).await?;
1823 Ok(())
1824 }
1825 async fn get_webhook(&self, id: &str) -> DbResult<Webhook> {
1826 let w = webhook::Entity::find()
1827 .filter(webhook::Column::Id.eq(
1828 TryInto::<Uuid>::try_into(id).map_err(|_| DbError::InvalidId(id.to_string()))?,
1829 ))
1830 .one(&self.db_con)
1831 .await?
1832 .ok_or(DbError::WebhookNotFound)?;
1833
1834 Ok(Webhook {
1835 id: Some(w.id.into()),
1836 name: w.name,
1837 event: w
1838 .event
1839 .as_str()
1840 .try_into()
1841 .map_err(|_| DbError::InvalidWebhookEvent(w.event))?,
1842 callback_url: w.callback_url,
1843 })
1844 }
1845 async fn get_all_webhooks(&self) -> DbResult<Vec<Webhook>> {
1846 let w = webhook::Entity::find().all(&self.db_con).await?;
1847
1848 Ok(w.into_iter()
1849 .filter_map(|w| {
1850 Some(Webhook {
1851 id: Some(w.id.into()),
1852 name: w.name,
1853 event: w.event.as_str().try_into().ok()?,
1855 callback_url: w.callback_url,
1856 })
1857 })
1858 .collect())
1859 }
1860 async fn add_webhook_queue(
1861 &self,
1862 event: WebhookEvent,
1863 payload: serde_json::Value,
1864 ) -> DbResult<()> {
1865 let w = webhook::Entity::find()
1866 .filter(webhook::Column::Event.eq(Into::<&str>::into(event)))
1867 .all(&self.db_con)
1868 .await?;
1869
1870 if w.is_empty() {
1871 return Ok(());
1872 }
1873
1874 let now = Utc::now();
1875
1876 let entries = w.iter().map(|w| webhook_queue::ActiveModel {
1877 webhook_fk: Set(w.id),
1878 payload: Set(payload.clone()),
1879 next_attempt: Set(now.into()),
1880 last_attempt: Set(None),
1881 ..Default::default()
1882 });
1883
1884 webhook_queue::Entity::insert_many(entries)
1885 .exec(&self.db_con)
1886 .await?;
1887 Ok(())
1888 }
1889 async fn get_pending_webhook_queue_entries(
1890 &self,
1891 timestamp: DateTime<Utc>,
1892 ) -> DbResult<Vec<WebhookQueue>> {
1893 let w = webhook_queue::Entity::find()
1894 .find_with_related(webhook::Entity)
1895 .filter(webhook_queue::Column::NextAttempt.lte(timestamp))
1896 .all(&self.db_con)
1897 .await?;
1898
1899 Ok(w.iter()
1900 .filter_map(|w| {
1901 Some(WebhookQueue {
1902 id: Into::<String>::into(w.0.id),
1903 callback_url: w.1.first()?.callback_url.clone(),
1904 payload: w.0.payload.clone(),
1905 last_attempt: w.0.last_attempt.map(Into::into),
1906 next_attempt: w.0.next_attempt.into(),
1907 })
1908 })
1909 .collect())
1910 }
1911 async fn update_webhook_queue(
1912 &self,
1913 id: &str,
1914 last_attempt: DateTime<Utc>,
1915 next_attempt: DateTime<Utc>,
1916 ) -> DbResult<()> {
1917 let w = webhook_queue::Entity::find()
1918 .filter(webhook_queue::Column::Id.eq(
1919 TryInto::<Uuid>::try_into(id).map_err(|_| DbError::InvalidId(id.to_string()))?,
1920 ))
1921 .one(&self.db_con)
1922 .await?
1923 .ok_or(DbError::WebhookNotFound)?;
1924
1925 let mut w: webhook_queue::ActiveModel = w.into();
1926 w.last_attempt = Set(Some(last_attempt.into()));
1927 w.next_attempt = Set(next_attempt.into());
1928 w.update(&self.db_con).await?;
1929 Ok(())
1930 }
1931 async fn delete_webhook_queue(&self, id: &str) -> DbResult<()> {
1932 let w = webhook_queue::Entity::find()
1933 .filter(webhook_queue::Column::Id.eq(
1934 TryInto::<Uuid>::try_into(id).map_err(|_| DbError::InvalidId(id.to_string()))?,
1935 ))
1936 .one(&self.db_con)
1937 .await?
1938 .ok_or(DbError::WebhookNotFound)?;
1939
1940 w.delete(&self.db_con).await?;
1941 Ok(())
1942 }
1943}
1944
1945async fn get_desc_for_crate_dep<C: ConnectionTrait>(
1948 db_con: &C,
1949 name: &str,
1950 registry: Option<&str>,
1951) -> DbResult<Option<String>> {
1952 let desc = if registry.unwrap_or_default() == "https://github.com/rust-lang/crates.io-index" {
1953 let krate = cratesio_crate::Entity::find()
1954 .filter(cratesio_crate::Column::Name.eq(name))
1955 .one(db_con)
1956 .await?;
1957 krate.and_then(|krate| krate.description)
1958 } else {
1959 let krate = krate::Entity::find()
1962 .filter(krate::Column::Name.eq(name))
1963 .one(db_con)
1964 .await?;
1965 krate.and_then(|krate| krate.description)
1966 };
1967
1968 Ok(desc)
1969}
1970
1971async fn insert_admin_credentials<C: ConnectionTrait>(
1972 db_con: &C,
1973 con_string: &ConString,
1974) -> DbResult<()> {
1975 let hashed_pwd = hash_pwd(&con_string.admin_pwd(), &con_string.salt());
1976
1977 let admin = user::ActiveModel {
1978 name: Set("admin".to_string()),
1979 pwd: Set(hashed_pwd),
1980 salt: Set(con_string.salt()),
1981 is_admin: Set(true),
1982 is_read_only: Set(false),
1983 ..Default::default()
1984 };
1985
1986 let res: InsertResult<user::ActiveModel> = user::Entity::insert(admin).exec(db_con).await?;
1987 let auth_token = hash_token(&con_string.admin_token());
1988
1989 let auth_token = auth_token::ActiveModel {
1990 name: Set("admin".to_string()),
1991 token: Set(auth_token),
1992 user_fk: Set(res.last_insert_id),
1993 ..Default::default()
1994 };
1995 auth_token::Entity::insert(auth_token).exec(db_con).await?;
1996
1997 Ok(())
1998}
1999
2000async fn no_user_exists<C: ConnectionTrait>(db_con: &C) -> DbResult<bool> {
2001 let id = user::Entity::find()
2002 .one(db_con)
2003 .await?
2004 .map(|model| model.id);
2005
2006 Ok(id.is_none())
2007}
2008
2009async fn add_owner_if_not_exists<C: ConnectionTrait>(
2010 db_con: &C,
2011 owner: &str,
2012 crate_id: i64,
2013) -> DbResult<()> {
2014 let user_fk = user::Entity::find()
2015 .filter(user::Column::Name.eq(owner))
2016 .one(db_con)
2017 .await?
2018 .map(|model| model.id)
2019 .ok_or_else(|| DbError::UserNotFound(owner.to_string()))?;
2020
2021 let owner = owner::Entity::find()
2022 .filter(owner::Column::CrateFk.eq(crate_id))
2023 .filter(owner::Column::UserFk.eq(user_fk))
2024 .one(db_con)
2025 .await?;
2026
2027 if owner.is_none() {
2028 let o = owner::ActiveModel {
2029 user_fk: Set(user_fk),
2030 crate_fk: Set(crate_id),
2031 ..Default::default()
2032 };
2033
2034 o.insert(db_con).await?;
2035 }
2036 Ok(())
2037}
2038
2039async fn add_crate_index<C: ConnectionTrait>(
2040 db_con: &C,
2041 pub_metadata: &PublishMetadata,
2042 cksum: &str,
2043 crate_id: i64,
2044) -> DbResult<()> {
2045 let index_data = IndexMetadata::from_reg_meta(pub_metadata, cksum);
2046
2047 let deps = if index_data.deps.is_empty() {
2048 None
2049 } else {
2050 let deps = serde_json::to_value(&index_data.deps)
2051 .map_err(|e| DbError::FailedToConvertToJson(e.to_string()))?;
2052 Some(deps)
2053 };
2054
2055 let features = serde_json::to_value(&index_data.features)
2056 .map_err(|e| DbError::FailedToConvertToJson(e.to_string()))?;
2057
2058 let ci = crate_index::ActiveModel {
2059 id: ActiveValue::default(),
2060 name: Set(index_data.name),
2061 vers: Set(index_data.vers),
2062 deps: Set(deps),
2063 cksum: Set(cksum.to_owned()),
2064 features: Set(Some(features)),
2065 yanked: ActiveValue::default(),
2066 pubtime: Set(index_data.pubtime.map(|dt| dt.naive_utc())),
2067 links: Set(index_data.links),
2068 v: Set(index_data.v.unwrap_or(1) as i32),
2069 crate_fk: Set(crate_id),
2070 };
2071
2072 ci.insert(db_con).await?;
2073 Ok(())
2074}
2075
2076async fn add_crate_metadata<C: ConnectionTrait>(
2077 db_con: &C,
2078 pub_metadata: &PublishMetadata,
2079 created: &str,
2080 crate_id: i64,
2081) -> DbResult<()> {
2082 let cm = crate_meta::ActiveModel {
2083 id: ActiveValue::default(),
2084 version: Set(pub_metadata.vers.clone()),
2085 created: Set(created.to_string()),
2086 downloads: Set(0),
2087 crate_fk: Set(crate_id),
2088 readme: Set(pub_metadata.readme.clone()),
2089 license: Set(pub_metadata.license.clone()),
2090 license_file: Set(pub_metadata.license_file.clone()),
2091 documentation: Set(pub_metadata.documentation.clone()),
2092 };
2093
2094 cm.insert(db_con).await?;
2095
2096 Ok(())
2097}
2098
2099async fn update_crate_categories<C: ConnectionTrait>(
2100 db_con: &C,
2101 pub_metadata: &PublishMetadata,
2102 crate_id: i64,
2103) -> DbResult<()> {
2104 let categories = pub_metadata.categories.clone();
2105
2106 crate_category_to_crate::Entity::delete_many()
2108 .filter(crate_category_to_crate::Column::CrateFk.eq(crate_id))
2109 .exec(db_con)
2110 .await?;
2111
2112 for category in categories {
2114 let category_fk = crate_category::Entity::find()
2115 .filter(crate_category::Column::Category.eq(category.clone()))
2116 .one(db_con)
2117 .await?
2118 .map(|model| model.id);
2119
2120 let category_fk = if let Some(category_fk) = category_fk {
2122 category_fk
2123 } else {
2124 let cc = crate_category::ActiveModel {
2125 id: ActiveValue::default(),
2126 category: Set(category.clone()),
2127 };
2128
2129 cc.insert(db_con).await?.id
2130 };
2131
2132 let cctc = crate_category_to_crate::ActiveModel {
2134 id: ActiveValue::default(),
2135 crate_fk: Set(crate_id),
2136 category_fk: Set(category_fk),
2137 };
2138 cctc.insert(db_con).await?;
2139 }
2140
2141 Ok(())
2142}
2143
2144async fn update_crate_keywords<C: ConnectionTrait>(
2145 db_con: &C,
2146 pub_metadata: &PublishMetadata,
2147 crate_id: i64,
2148) -> DbResult<()> {
2149 let keywords = pub_metadata.keywords.clone();
2150
2151 crate_keyword_to_crate::Entity::delete_many()
2153 .filter(crate_keyword_to_crate::Column::CrateFk.eq(crate_id))
2154 .exec(db_con)
2155 .await?;
2156
2157 for keyword in keywords {
2159 let keyword_fk = crate_keyword::Entity::find()
2160 .filter(crate_keyword::Column::Keyword.eq(keyword.clone()))
2161 .one(db_con)
2162 .await?
2163 .map(|model| model.id);
2164
2165 let keyword_fk = if let Some(keyword_fk) = keyword_fk {
2167 keyword_fk
2168 } else {
2169 let ck = crate_keyword::ActiveModel {
2170 id: ActiveValue::default(),
2171 keyword: Set(keyword.clone()),
2172 };
2173
2174 ck.insert(db_con).await?.id
2175 };
2176
2177 let cktc = crate_keyword_to_crate::ActiveModel {
2179 id: ActiveValue::default(),
2180 crate_fk: Set(crate_id),
2181 keyword_fk: Set(keyword_fk),
2182 };
2183 cktc.insert(db_con).await?;
2184 }
2185
2186 Ok(())
2187}
2188
2189async fn update_crate_authors<C: ConnectionTrait>(
2190 db_con: &C,
2191 pub_metadata: &PublishMetadata,
2192 crate_id: i64,
2193) -> DbResult<()> {
2194 let authors = pub_metadata.authors.clone().unwrap_or_default();
2195
2196 crate_author_to_crate::Entity::delete_many()
2198 .filter(crate_author_to_crate::Column::CrateFk.eq(crate_id))
2199 .exec(db_con)
2200 .await?;
2201
2202 for author in authors {
2204 let author_fk = crate_author::Entity::find()
2205 .filter(crate_author::Column::Author.eq(author.clone()))
2206 .one(db_con)
2207 .await?
2208 .map(|model| model.id);
2209
2210 let author_fk = if let Some(author_fk) = author_fk {
2212 author_fk
2213 } else {
2214 let ca = crate_author::ActiveModel {
2215 id: ActiveValue::default(),
2216 author: Set(author.clone()),
2217 };
2218
2219 ca.insert(db_con).await?.id
2220 };
2221
2222 let catc = crate_author_to_crate::ActiveModel {
2224 id: ActiveValue::default(),
2225 crate_fk: Set(crate_id),
2226 author_fk: Set(author_fk),
2227 };
2228 catc.insert(db_con).await?;
2229 }
2230
2231 Ok(())
2232}
2233
2234async fn compute_etag<C: ConnectionTrait>(
2235 db_con: &C,
2236 crate_name: &str,
2237 crate_id: i64,
2238) -> DbResult<String> {
2239 let crate_indices = crate_index::Entity::find()
2240 .filter(crate_index::Column::CrateFk.eq(crate_id))
2241 .all(db_con)
2242 .await?;
2243
2244 let index_metadata = crate_index_model_to_index_metadata(crate_name, crate_indices)?;
2245 let data = index_metadata_to_bytes(&index_metadata)?;
2246
2247 Ok(sha256::digest(data))
2248}
2249
2250fn index_metadata_to_bytes(index_metadata: &[IndexMetadata]) -> DbResult<Vec<u8>> {
2251 IndexMetadata::serialize_indices(index_metadata)
2252 .map(String::into_bytes)
2253 .map_err(|e| DbError::FailedToConvertToJson(format!("{e}")))
2254}
2255
2256fn crate_index_model_to_index_metadata(
2257 crate_name: &str,
2258 crate_indices: Vec<crate_index::Model>,
2259) -> DbResult<Vec<IndexMetadata>> {
2260 let mut index_metadata = vec![];
2261 for ci in crate_indices {
2262 let deps = match ci.deps {
2263 Some(ref deps) => serde_json::value::from_value(deps.to_owned()).map_err(|e| {
2264 DbError::FailedToConvertFromJson(format!(
2265 "Failed to deserialize crate dependencies of {crate_name}: {e}"
2266 ))
2267 })?,
2268 None => vec![],
2269 };
2270 let features = ci.features.clone().unwrap_or_default();
2271 let features = serde_json::value::from_value(features).map_err(|e| {
2272 DbError::FailedToConvertFromJson(format!(
2273 "Failed to deserialize crate features of {crate_name}: {e}"
2274 ))
2275 })?;
2276
2277 let cm = IndexMetadata {
2278 name: ci.name,
2279 vers: ci.vers,
2280 deps,
2281 cksum: ci.cksum,
2282 features,
2283 yanked: ci.yanked,
2284 pubtime: ci.pubtime.map(|dt| dt.and_utc()),
2285 links: ci.links,
2286 v: Some(ci.v as u32),
2287 features2: None,
2288 };
2289 index_metadata.push(cm);
2290 }
2291 Ok(index_metadata)
2292}
2293
2294fn cratesio_index_model_to_index_metadata(
2295 crate_name: &NormalizedName,
2296 crate_indices: Vec<cratesio_index::Model>,
2297) -> DbResult<Vec<IndexMetadata>> {
2298 let mut index_metadata = vec![];
2299 for ci in crate_indices {
2300 let deps = match ci.deps {
2301 Some(ref deps) => serde_json::value::from_value(deps.to_owned()).map_err(|e| {
2302 DbError::FailedToConvertFromJson(format!(
2303 "Failed to deserialize crate dependencies of {crate_name}: {e}"
2304 ))
2305 })?,
2306 None => vec![],
2307 };
2308 let features = ci.features.clone().unwrap_or_default();
2309 let features = serde_json::value::from_value(features).map_err(|e| {
2310 DbError::FailedToConvertFromJson(format!(
2311 "Failed to deserialize crate features of {crate_name}: {e}"
2312 ))
2313 })?;
2314
2315 let features2 = ci.features2.clone().unwrap_or_default();
2316 let features2 = serde_json::value::from_value(features2).map_err(|e| {
2317 DbError::FailedToConvertFromJson(format!(
2318 "Failed to deserialize crate features of {crate_name}: {e}"
2319 ))
2320 })?;
2321
2322 let cm = IndexMetadata {
2323 name: ci.name,
2324 vers: ci.vers.clone(),
2325 deps,
2326 cksum: ci.cksum.clone(),
2327 pubtime: ci.pubtime.map(|dt| dt.and_utc()),
2328 features,
2329 features2,
2330 yanked: ci.yanked,
2331 links: ci.links.clone(),
2332 v: Some(ci.v as u32),
2333 };
2334 index_metadata.push(cm);
2335 }
2336 Ok(index_metadata)
2337}
2338
2339async fn update_etag<C: ConnectionTrait>(
2340 db_con: &C,
2341 crate_name: &str,
2342 crate_id: i64,
2343) -> DbResult<()> {
2344 let etag = compute_etag(db_con, crate_name, crate_id).await?;
2345 let krate = krate::Entity::find()
2346 .filter(krate::Column::Id.eq(crate_id))
2347 .one(db_con)
2348 .await?
2349 .ok_or(DbError::CrateNotFound(crate_name.to_string()))?;
2350 let mut krate: krate::ActiveModel = krate.into();
2351 krate.e_tag = Set(etag);
2352 krate.update(db_con).await?;
2353 Ok(())
2354}
2355
2356async fn get_max_version_from_id<C: ConnectionTrait>(
2357 db_con: &C,
2358 crate_id: i64,
2359) -> DbResult<Version> {
2360 let krate = krate::Entity::find_by_id(crate_id).one(db_con).await?;
2361
2362 let k = krate.ok_or(DbError::FailedToGetMaxVersionById(crate_id))?;
2363 let v = Version::try_from(&k.max_version)
2364 .map_err(|_| DbError::FailedToGetMaxVersionById(crate_id))?;
2365 Ok(v)
2366}
2367
2368pub mod test_utils {
2371
2372 use super::*;
2373
2374 pub async fn test_add_cached_crate_with_downloads(
2375 db: &Database,
2376 name: &str,
2377 version: &str,
2378 downloads: u64,
2379 ) -> DbResult<()> {
2380 let _ = test_add_cached_crate(db, name, version).await?;
2381
2382 let krate = cratesio_crate::Entity::find()
2383 .filter(cratesio_crate::Column::Name.eq(name))
2384 .one(&db.db_con)
2385 .await?
2386 .ok_or(DbError::CrateNotFound(name.to_string()))?;
2387
2388 let total_downloads = krate.total_downloads as u64;
2389
2390 let mut krate: cratesio_crate::ActiveModel = krate.into();
2391 krate.total_downloads = Set((total_downloads + downloads) as i64);
2392 krate.update(&db.db_con).await?;
2393
2394 Ok(())
2395 }
2396
2397 pub async fn test_add_cached_crate(
2398 db: &Database,
2399 name: &str,
2400 version: &str,
2401 ) -> DbResult<Prefetch> {
2402 let etag = "etag";
2403 let last_modified = "last_modified";
2404 let description = Some(String::from("description"));
2405 let indices = vec![IndexMetadata {
2406 name: name.to_string(),
2407 vers: version.to_string(),
2408 deps: vec![],
2409 cksum: "cksum".to_string(),
2410 features: BTreeMap::new(),
2411 features2: None,
2412 pubtime: None,
2413 yanked: false,
2414 links: None,
2415 v: Some(1),
2416 }];
2417
2418 db.add_cratesio_prefetch_data(
2419 &OriginalName::from_unchecked(name.to_string()),
2420 etag,
2421 last_modified,
2422 description,
2423 &indices,
2424 )
2425 .await
2426 }
2427
2428 pub async fn test_add_crate(
2429 db: &Database,
2430 name: &str,
2431 owner: &str,
2432 version: &Version,
2433 created: &DateTime<Utc>,
2434 ) -> DbResult<i64> {
2435 let pm = PublishMetadata {
2436 name: name.to_string(),
2437 vers: version.to_string(),
2438 ..PublishMetadata::default()
2439 };
2440 let user = user::Entity::find()
2441 .filter(user::Column::Name.eq(owner))
2442 .one(&db.db_con)
2443 .await?;
2444 if user.is_none() {
2445 db.add_user(name, "pwd", "salt", false, false).await?;
2446 }
2447
2448 db.add_crate(&pm, "cksum", created, owner).await
2449 }
2450
2451 pub async fn test_add_crate_with_downloads(
2452 db: &Database,
2453 name: &str,
2454 owner: &str,
2455 version: &Version,
2456 created: &DateTime<Utc>,
2457 downloads: Option<i64>,
2458 ) -> DbResult<i64> {
2459 let pm = PublishMetadata {
2460 name: name.to_string(),
2461 vers: version.to_string(),
2462 ..PublishMetadata::default()
2463 };
2464 let user = user::Entity::find()
2465 .filter(user::Column::Name.eq(owner))
2466 .one(&db.db_con)
2467 .await?;
2468 if user.is_none() {
2469 db.add_user(name, "pwd", "salt", false, false).await?;
2470 }
2471
2472 db.add_crate(&pm, "cksum", created, owner).await?;
2473 let (cm, krate) = crate_meta::Entity::find()
2474 .find_also_related(krate::Entity)
2475 .filter(krate::Column::Name.eq(name))
2476 .filter(crate_meta::Column::Version.eq(version.to_string()))
2477 .one(&db.db_con)
2478 .await?
2479 .ok_or(DbError::CrateNotFound(name.to_string()))?;
2480 let mut cm: crate_meta::ActiveModel = cm.into();
2481
2482 let current_downloads = krate.as_ref().unwrap().total_downloads;
2483 let crate_id = krate.as_ref().unwrap().id;
2484
2485 let mut krate: krate::ActiveModel = krate.unwrap().into();
2486 krate.total_downloads = Set(current_downloads + downloads.unwrap_or(0));
2487 krate.update(&db.db_con).await?;
2488 cm.downloads = Set(downloads.unwrap_or_default());
2489 cm.update(&db.db_con).await?;
2490 Ok(crate_id)
2491 }
2492
2493 pub async fn test_add_crate_meta(
2494 db: &Database,
2495 crate_id: i64,
2496 version: &str,
2497 created: &DateTime<Utc>,
2498 downloads: Option<i64>,
2499 ) -> DbResult<()> {
2500 let cm = crate_meta::ActiveModel {
2501 id: ActiveValue::default(),
2502 version: Set(version.to_string()),
2503 created: Set(created.to_string()),
2504 downloads: Set(downloads.unwrap_or_default()),
2505 crate_fk: Set(crate_id),
2506 ..Default::default()
2507 };
2508
2509 cm.insert(&db.db_con).await?;
2510
2511 Ok(())
2512 }
2513
2514 pub async fn test_delete_crate_index(db: &Database, crate_id: i64) -> DbResult<()> {
2515 crate_index::Entity::delete_many()
2516 .filter(crate_index::Column::CrateFk.eq(crate_id))
2517 .exec(&db.db_con)
2518 .await?;
2519 Ok(())
2520 }
2521
2522 pub async fn clean_db(db: &Database, session_age: std::time::Duration) -> DbResult<()> {
2523 let session_age = chrono::Duration::from_std(session_age).unwrap();
2524 let now = std::ops::Add::add(Utc::now(), session_age)
2525 .format(DB_DATE_FORMAT)
2526 .to_string();
2527
2528 session::Entity::delete_many()
2529 .filter(Expr::col(session::Column::Created).lt(now))
2530 .exec(&db.db_con)
2531 .await?;
2532
2533 Ok(())
2534 }
2535
2536 pub async fn get_crate_meta_list(db: &Database, crate_id: i64) -> DbResult<Vec<CrateMeta>> {
2537 let cm: Vec<(crate_meta::Model, Option<krate::Model>)> = crate_meta::Entity::find()
2538 .find_also_related(krate::Entity)
2539 .filter(crate_meta::Column::CrateFk.eq(crate_id))
2540 .all(&db.db_con)
2541 .await?;
2542
2543 let crate_metas: Vec<CrateMeta> = cm
2544 .into_iter()
2545 .map(|(m, c)| CrateMeta {
2546 name: c.unwrap().name, id: m.id,
2548 version: m.version,
2549 created: m.created,
2550 downloads: m.downloads,
2551 crate_fk: m.crate_fk,
2552 })
2553 .collect();
2554
2555 Ok(crate_metas)
2556 }
2557}