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 crate_version_exists(&self, crate_id: i64, version: &str) -> DbResult<bool> {
631 let cm = crate_meta::Entity::find()
632 .filter(
633 Cond::all()
634 .add(crate_meta::Column::CrateFk.eq(crate_id))
635 .add(crate_meta::Column::Version.eq(version)),
636 )
637 .one(&self.db_con)
638 .await?;
639
640 Ok(cm.is_some())
641 }
642
643 async fn get_max_version_from_id(&self, crate_id: i64) -> DbResult<Version> {
644 get_max_version_from_id(&self.db_con, crate_id).await
645 }
646
647 async fn get_max_version_from_name(&self, crate_name: &NormalizedName) -> DbResult<Version> {
648 let krate = krate::Entity::find()
649 .filter(krate::Column::Name.eq(crate_name.to_string()))
650 .one(&self.db_con)
651 .await?;
652
653 let k =
654 krate.ok_or_else(|| DbError::FailedToGetMaxVersionByName(crate_name.to_string()))?;
655 let v = Version::try_from(&k.max_version)
656 .map_err(|_| DbError::FailedToGetMaxVersionByName(crate_name.to_string()))?;
657 Ok(v)
658 }
659
660 async fn update_max_version(&self, crate_id: i64, version: &Version) -> DbResult<()> {
661 let krate = krate::Entity::find_by_id(crate_id)
662 .one(&self.db_con)
663 .await?
664 .ok_or(DbError::CrateNotFoundWithId(crate_id))?;
665
666 let mut k: krate::ActiveModel = krate.into();
667 k.max_version = Set(version.to_string());
668 k.update(&self.db_con).await?;
669
670 Ok(())
671 }
672
673 async fn add_auth_token(&self, name: &str, token: &str, user: &str) -> DbResult<()> {
674 let hashed_token = hash_token(token);
675
676 let user = user::Entity::find()
677 .filter(user::Column::Name.eq(user))
678 .one(&self.db_con)
679 .await?
680 .ok_or_else(|| DbError::UserNotFound(user.to_string()))?;
681
682 let at = auth_token::ActiveModel {
683 name: Set(name.to_owned()),
684 token: Set(hashed_token.clone()),
685 user_fk: Set(user.id),
686 ..Default::default()
687 };
688
689 at.insert(&self.db_con).await?;
690
691 Ok(())
692 }
693
694 async fn get_user_from_token(&self, token: &str) -> DbResult<User> {
695 let token = hash_token(token);
696
697 let u = user::Entity::find()
698 .join(JoinType::InnerJoin, user::Relation::AuthToken.def())
699 .filter(Expr::col((AuthTokenIden::Table, AuthTokenIden::Token)).eq(token))
700 .one(&self.db_con)
701 .await?
702 .ok_or(DbError::TokenNotFound)?;
703
704 Ok(User {
705 id: u.id as i32,
706 name: u.name,
707 pwd: u.pwd,
708 salt: u.salt,
709 is_admin: u.is_admin,
710 is_read_only: u.is_read_only,
711 })
712 }
713
714 async fn get_user(&self, name: &str) -> DbResult<User> {
715 let u = user::Entity::find()
716 .filter(user::Column::Name.eq(name))
717 .one(&self.db_con)
718 .await?
719 .ok_or_else(|| DbError::UserNotFound(name.to_owned()))?;
720
721 Ok(User {
722 id: u.id as i32,
723 name: u.name,
724 pwd: u.pwd,
725 salt: u.salt,
726 is_admin: u.is_admin,
727 is_read_only: u.is_read_only,
728 })
729 }
730
731 async fn get_group(&self, name: &str) -> DbResult<Group> {
732 let g = group::Entity::find()
733 .filter(group::Column::Name.eq(name))
734 .one(&self.db_con)
735 .await?
736 .ok_or_else(|| DbError::GroupNotFound(name.to_owned()))?;
737
738 Ok(Group {
739 id: g.id as i32,
740 name: g.name,
741 })
742 }
743
744 async fn get_auth_tokens(&self, user_name: &str) -> DbResult<Vec<AuthToken>> {
745 let at: Vec<auth_token::Model> = auth_token::Entity::find()
746 .join(JoinType::InnerJoin, auth_token::Relation::User.def())
747 .filter(user::Column::Name.eq(user_name))
748 .all(&self.db_con)
749 .await?;
750
751 Ok(at
752 .into_iter()
753 .map(|x| AuthToken::new(x.id as i32, x.name, x.token))
754 .collect())
755 }
756
757 async fn delete_auth_token(&self, id: i32) -> DbResult<()> {
758 auth_token::Entity::delete_by_id(id as i64)
759 .exec(&self.db_con)
760 .await?;
761 Ok(())
762 }
763
764 async fn delete_owner(&self, crate_name: &str, owner: &str) -> DbResult<()> {
765 let owner = owner::Entity::find()
766 .join(JoinType::InnerJoin, owner::Relation::Krate.def())
767 .join(JoinType::InnerJoin, owner::Relation::User.def())
768 .filter(
769 Cond::all()
770 .add(krate::Column::Name.eq(crate_name))
771 .add(user::Column::Name.eq(owner)),
772 )
773 .one(&self.db_con)
774 .await?
775 .ok_or_else(|| DbError::OwnerNotFound(owner.to_string()))?;
776
777 owner.delete(&self.db_con).await?;
778
779 Ok(())
780 }
781
782 async fn delete_crate_user(&self, crate_name: &str, user: &str) -> DbResult<()> {
783 let user = crate_user::Entity::find()
784 .join(JoinType::InnerJoin, crate_user::Relation::Krate.def())
785 .join(JoinType::InnerJoin, crate_user::Relation::User.def())
786 .filter(
787 Cond::all()
788 .add(krate::Column::Name.eq(crate_name))
789 .add(user::Column::Name.eq(user)),
790 )
791 .one(&self.db_con)
792 .await?
793 .ok_or_else(|| DbError::UserNotFound(user.to_string()))?;
794
795 user.delete(&self.db_con).await?;
796
797 Ok(())
798 }
799
800 async fn delete_crate_group(&self, crate_name: &NormalizedName, group: &str) -> DbResult<()> {
801 let group = crate_group::Entity::find()
802 .join(JoinType::InnerJoin, crate_group::Relation::Krate.def())
803 .join(JoinType::InnerJoin, crate_group::Relation::Group.def())
804 .filter(
805 Cond::all()
806 .add(krate::Column::Name.eq(crate_name.to_string()))
807 .add(group::Column::Name.eq(group)),
808 )
809 .one(&self.db_con)
810 .await?
811 .ok_or_else(|| DbError::GroupNotFound(group.to_string()))?;
812
813 group.delete(&self.db_con).await?;
814
815 Ok(())
816 }
817
818 async fn delete_group_user(&self, group_name: &str, user: &str) -> DbResult<()> {
819 let user = group_user::Entity::find()
820 .join(JoinType::InnerJoin, group_user::Relation::Group.def())
821 .join(JoinType::InnerJoin, group_user::Relation::User.def())
822 .filter(
823 Cond::all()
824 .add(group::Column::Name.eq(group_name))
825 .add(user::Column::Name.eq(user)),
826 )
827 .one(&self.db_con)
828 .await?
829 .ok_or_else(|| DbError::UserNotFound(user.to_string()))?;
830
831 user.delete(&self.db_con).await?;
832
833 Ok(())
834 }
835
836 async fn add_user(
837 &self,
838 name: &str,
839 pwd: &str,
840 salt: &str,
841 is_admin: bool,
842 is_read_only: bool,
843 ) -> DbResult<()> {
844 let hashed_pwd = hash_pwd(pwd, salt);
845
846 let u = user::ActiveModel {
847 name: Set(name.to_owned()),
848 pwd: Set(hashed_pwd),
849 salt: Set(salt.to_owned()),
850 is_admin: Set(is_admin),
851 is_read_only: Set(is_read_only),
852 ..Default::default()
853 };
854
855 u.insert(&self.db_con).await?;
856 Ok(())
857 }
858
859 async fn add_group(&self, name: &str) -> DbResult<()> {
860 let g = group::ActiveModel {
861 name: Set(name.to_owned()),
862 ..Default::default()
863 };
864
865 g.insert(&self.db_con).await?;
866 Ok(())
867 }
868
869 async fn get_users(&self) -> DbResult<Vec<User>> {
870 let users = user::Entity::find()
871 .order_by_asc(user::Column::Name)
872 .all(&self.db_con)
873 .await?;
874
875 Ok(users
876 .into_iter()
877 .map(|u| User {
878 id: u.id as i32,
879 name: u.name,
880 pwd: u.pwd,
881 salt: u.salt,
882 is_admin: u.is_admin,
883 is_read_only: u.is_read_only,
884 })
885 .collect())
886 }
887
888 async fn get_groups(&self) -> DbResult<Vec<Group>> {
889 let groups = group::Entity::find()
890 .order_by_asc(group::Column::Name)
891 .all(&self.db_con)
892 .await?;
893
894 Ok(groups
895 .into_iter()
896 .map(|g| Group {
897 id: g.id as i32,
898 name: g.name,
899 })
900 .collect())
901 }
902
903 async fn get_total_unique_crates(&self) -> DbResult<u32> {
904 #[derive(Debug, PartialEq, FromQueryResult)]
905 struct SelectResult {
906 count: Option<i64>,
907 }
908
909 let stmt = Query::select()
910 .expr_as(
911 Expr::col((CrateIden::Table, CrateIden::Id)).count(),
912 Alias::new("count"),
913 )
914 .from(CrateIden::Table)
915 .to_owned();
916
917 let builder = self.db_con.get_database_backend();
918 let result = SelectResult::find_by_statement(builder.build(&stmt))
919 .one(&self.db_con)
920 .await?
921 .ok_or(DbError::FailedToCountCrates)?
922 .count
923 .ok_or(DbError::FailedToCountCrates)?;
924
925 Ok(result as u32)
926 }
927
928 async fn get_total_crate_versions(&self) -> DbResult<u32> {
929 #[derive(Debug, PartialEq, FromQueryResult)]
930 struct SelectResult {
931 count: Option<i64>,
932 }
933
934 let stmt = Query::select()
935 .expr_as(
936 Expr::col((CrateMetaIden::Table, CrateMetaIden::Id)).count(),
937 Alias::new("count"),
938 )
939 .from(CrateMetaIden::Table)
940 .to_owned();
941
942 let builder = self.db_con.get_database_backend();
943 let result = SelectResult::find_by_statement(builder.build(&stmt))
944 .one(&self.db_con)
945 .await?
946 .ok_or(DbError::FailedToCountCrateVersions)?
947 .count
948 .ok_or(DbError::FailedToCountCrateVersions)?;
949
950 Ok(result as u32)
951 }
952
953 async fn get_total_downloads(&self) -> DbResult<u64> {
954 #[derive(FromQueryResult)]
955 struct Model {
956 total_downloads: i64,
957 }
958
959 let total_downloads = krate::Entity::find()
960 .select_only()
961 .column(krate::Column::TotalDownloads)
962 .into_model::<Model>()
963 .all(&self.db_con)
964 .await?;
965
966 Ok(total_downloads
967 .iter()
968 .map(|m| m.total_downloads as u64)
969 .sum())
970 }
971
972 async fn get_top_crates_downloads(&self, top: u32) -> DbResult<Vec<(String, u64)>> {
973 #[derive(Debug, PartialEq, FromQueryResult)]
974 struct SelectResult {
975 original_name: String,
976 total_downloads: i64,
977 }
978
979 let stmt = Query::select()
980 .columns(vec![CrateIden::OriginalName, CrateIden::TotalDownloads])
981 .from(CrateIden::Table)
982 .order_by(CrateIden::TotalDownloads, Order::Desc)
983 .limit(top as u64)
984 .to_owned();
985
986 let builder = self.db_con.get_database_backend();
987 let result = SelectResult::find_by_statement(builder.build(&stmt))
988 .all(&self.db_con)
989 .await?;
990
991 Ok(result
992 .iter()
993 .map(|x| (x.original_name.clone(), x.total_downloads as u64))
994 .collect())
995 }
996
997 async fn get_crate_summaries(&self) -> DbResult<Vec<CrateSummary>> {
998 let krates = krate::Entity::find()
999 .order_by(krate::Column::Name, Order::Asc)
1000 .all(&self.db_con)
1001 .await?;
1002
1003 let krates = krates
1004 .iter()
1005 .map(|c| CrateSummary {
1006 name: c.name.clone(),
1007 max_version: c.max_version.clone(),
1008 last_updated: c.last_updated.clone(),
1009 total_downloads: c.total_downloads,
1010 })
1011 .collect();
1012
1013 Ok(krates)
1014 }
1015
1016 async fn add_doc_queue(
1017 &self,
1018 krate: &NormalizedName,
1019 version: &Version,
1020 path: &Path,
1021 ) -> DbResult<()> {
1022 let s = doc_queue::ActiveModel {
1023 krate: Set(krate.to_string()),
1024 version: Set(version.to_string()),
1025 path: Set(path.to_string_lossy().to_string()),
1026 ..Default::default()
1027 };
1028
1029 s.insert(&self.db_con).await?;
1030 Ok(())
1031 }
1032
1033 async fn delete_doc_queue(&self, id: i64) -> DbResult<()> {
1034 DocQueue::delete_by_id(id).exec(&self.db_con).await?;
1035 Ok(())
1036 }
1037
1038 async fn get_doc_queue(&self) -> DbResult<Vec<DocQueueEntry>> {
1039 let entities = DocQueue::find().all(&self.db_con).await?;
1040
1041 Ok(entities.into_iter().map(DocQueueEntry::from).collect())
1042 }
1043
1044 async fn delete_crate(&self, krate: &NormalizedName, version: &Version) -> DbResult<()> {
1045 let txn = self.db_con.begin().await?;
1046
1047 let crate_meta_version = crate_meta::Entity::find()
1049 .join(JoinType::InnerJoin, crate_meta::Relation::Krate.def())
1050 .filter(krate::Column::Name.eq(krate.to_string()))
1051 .filter(crate_meta::Column::Version.eq(version.to_string()))
1052 .one(&txn)
1053 .await?
1054 .ok_or_else(|| DbError::CrateMetaNotFound(krate.to_string(), version.to_string()))?;
1055 let crate_id = crate_meta_version.crate_fk;
1056 let current_max_version = get_max_version_from_id(&txn, crate_id).await?;
1057 crate_meta_version.delete(&txn).await?;
1058
1059 let crate_index_version = crate_index::Entity::find()
1061 .join(JoinType::InnerJoin, crate_index::Relation::Krate.def())
1062 .filter(krate::Column::Name.eq(krate.to_string()))
1063 .filter(crate_index::Column::Vers.eq(version.to_string()))
1064 .one(&txn)
1065 .await?
1066 .ok_or_else(|| DbError::CrateIndexNotFound(krate.to_string(), version.to_string()))?;
1067 crate_index_version.delete(&txn).await?;
1068
1069 let crate_meta_rows = crate_meta::Entity::find()
1072 .join(JoinType::InnerJoin, crate_meta::Relation::Krate.def())
1073 .filter(krate::Column::Name.eq(krate.to_string()))
1074 .all(&txn)
1075 .await?;
1076
1077 if crate_meta_rows.is_empty() {
1078 krate::Entity::delete_many()
1079 .filter(krate::Column::Name.eq(krate.to_string()))
1080 .exec(&txn)
1081 .await?;
1082 } else {
1083 let c = krate::Entity::find_by_id(crate_id)
1084 .one(&txn)
1085 .await?
1086 .ok_or(DbError::CrateNotFoundWithId(crate_id))?;
1087 let mut c: krate::ActiveModel = c.into();
1088
1089 if version == ¤t_max_version {
1091 let new_max_version = crate_meta_rows
1092 .iter()
1093 .map(|cm| Version::from_unchecked_str(&cm.version))
1094 .max()
1095 .unwrap(); c.max_version = Set(new_max_version.to_string());
1097 }
1098 let etag = compute_etag(&txn, krate, crate_id).await?;
1100 c.e_tag = Set(etag);
1101 c.update(&txn).await?;
1102 }
1103
1104 txn.commit().await?;
1105
1106 Ok(())
1107 }
1108
1109 async fn get_crate_meta_list(&self, crate_name: &NormalizedName) -> DbResult<Vec<CrateMeta>> {
1110 let crate_meta = crate_meta::Entity::find()
1111 .join(JoinType::InnerJoin, crate_meta::Relation::Krate.def())
1112 .filter(krate::Column::Name.eq(crate_name.to_string()))
1113 .all(&self.db_con)
1114 .await?;
1115
1116 let crate_meta = crate_meta
1117 .into_iter()
1118 .map(|cm| CrateMeta {
1119 name: crate_name.to_string(),
1120 id: cm.id,
1121 version: cm.version,
1122 created: cm.created,
1123 downloads: cm.downloads,
1124 crate_fk: cm.crate_fk,
1125 })
1126 .collect();
1127
1128 Ok(crate_meta)
1129 }
1130
1131 async fn update_last_updated(&self, id: i64, last_updated: &DateTime<Utc>) -> DbResult<()> {
1132 let krate = krate::Entity::find_by_id(id)
1133 .one(&self.db_con)
1134 .await?
1135 .ok_or(DbError::CrateNotFoundWithId(id))?;
1136
1137 let date = last_updated.format(DB_DATE_FORMAT).to_string();
1138
1139 let mut krate: krate::ActiveModel = krate.into();
1140 krate.last_updated = Set(date);
1141 krate.update(&self.db_con).await?;
1142
1143 Ok(())
1144 }
1145
1146 async fn search_in_crate_name(
1147 &self,
1148 contains: &str,
1149 cache: bool,
1150 ) -> DbResult<Vec<CrateOverview>> {
1151 let mut stmt_kellnr = Query::select()
1152 .expr_as(Expr::col(CrateIden::OriginalName), Alias::new("name"))
1153 .expr_as(Expr::col(CrateIden::MaxVersion), Alias::new("version"))
1154 .expr_as(Expr::col(CrateIden::LastUpdated), Alias::new("date"))
1155 .expr_as(
1156 Expr::col(CrateIden::TotalDownloads),
1157 Alias::new("total_downloads"),
1158 )
1159 .expr_as(Expr::col(CrateIden::Description), Alias::new("description"))
1160 .expr_as(
1161 Expr::col(CrateMetaIden::Documentation),
1162 Alias::new("documentation"),
1163 )
1164 .expr_as(Expr::cust("false"), Alias::new("is_cache"))
1165 .from(CrateMetaIden::Table)
1166 .inner_join(
1167 CrateIden::Table,
1168 Expr::col((CrateMetaIden::Table, CrateMetaIden::CrateFk))
1169 .equals((CrateIden::Table, CrateIden::Id)),
1170 )
1171 .and_where(Expr::col((CrateIden::Table, CrateIden::Name)).like(format!("%{contains}%")))
1172 .and_where(
1173 Expr::col((CrateMetaIden::Table, CrateMetaIden::Version))
1174 .equals((CrateIden::Table, CrateIden::MaxVersion)),
1175 )
1176 .to_owned();
1177
1178 let stmt = if !cache {
1179 stmt_kellnr
1180 .order_by(CrateIden::OriginalName, Order::Asc)
1181 .to_owned()
1182 } else {
1183 stmt_kellnr
1184 .union(
1185 UnionType::All,
1186 Query::select()
1187 .expr_as(Expr::col(CratesIoIden::OriginalName), Alias::new("name"))
1188 .expr_as(Expr::col(CratesIoIden::MaxVersion), Alias::new("version"))
1189 .expr_as(Expr::col(CratesIoIden::LastModified), Alias::new("date"))
1190 .expr_as(
1191 Expr::col(CratesIoIden::TotalDownloads),
1192 Alias::new("total_downloads"),
1193 )
1194 .expr_as(
1195 Expr::col(CratesIoIden::Description),
1196 Alias::new("description"),
1197 )
1198 .expr_as(
1199 Expr::col(CratesIoMetaIden::Documentation),
1200 Alias::new("documentation"),
1201 )
1202 .expr_as(Expr::cust("true"), Alias::new("is_cache"))
1203 .from(CratesIoMetaIden::Table)
1204 .inner_join(
1205 CratesIoIden::Table,
1206 Expr::col((CratesIoMetaIden::Table, CratesIoMetaIden::CratesIoFk))
1207 .equals((CratesIoIden::Table, CratesIoIden::Id)),
1208 )
1209 .and_where(
1210 Expr::col((CratesIoMetaIden::Table, CratesIoMetaIden::Version))
1211 .equals((CratesIoIden::Table, CratesIoIden::MaxVersion)),
1212 )
1213 .and_where(
1214 Expr::col((CratesIoIden::Table, CrateIden::OriginalName))
1215 .like(format!("%{contains}%")),
1216 )
1217 .to_owned(),
1218 )
1219 .order_by(Alias::new("name"), Order::Asc)
1220 .to_owned()
1221 };
1222 let builder = self.db_con.get_database_backend();
1223 let result = CrateOverview::find_by_statement(builder.build(&stmt))
1224 .all(&self.db_con)
1225 .await?;
1226
1227 Ok(result)
1228 }
1229
1230 async fn get_crate_overview_list(
1231 &self,
1232 limit: u64,
1233 offset: u64,
1234 cache: bool,
1235 ) -> DbResult<Vec<CrateOverview>> {
1236 let mut stmt_kellnr = Query::select()
1237 .expr_as(Expr::col(CrateIden::OriginalName), Alias::new("name"))
1238 .expr_as(Expr::col(CrateIden::MaxVersion), Alias::new("version"))
1239 .expr_as(Expr::col(CrateIden::LastUpdated), Alias::new("date"))
1240 .expr_as(
1241 Expr::col(CrateIden::TotalDownloads),
1242 Alias::new("total_downloads"),
1243 )
1244 .expr_as(Expr::col(CrateIden::Description), Alias::new("description"))
1245 .expr_as(
1246 Expr::col(CrateMetaIden::Documentation),
1247 Alias::new("documentation"),
1248 )
1249 .expr_as(Expr::cust("false"), Alias::new("is_cache"))
1250 .from(CrateMetaIden::Table)
1251 .inner_join(
1252 CrateIden::Table,
1253 Expr::col((CrateMetaIden::Table, CrateMetaIden::CrateFk))
1254 .equals((CrateIden::Table, CrateIden::Id)),
1255 )
1256 .and_where(
1257 Expr::col((CrateMetaIden::Table, CrateMetaIden::Version))
1258 .equals((CrateIden::Table, CrateIden::MaxVersion)),
1259 )
1260 .to_owned();
1261
1262 let stmt = if !cache {
1263 stmt_kellnr
1264 .order_by(CrateIden::OriginalName, Order::Asc)
1265 .limit(limit)
1266 .offset(offset)
1267 .to_owned()
1268 } else {
1269 stmt_kellnr
1270 .union(
1271 UnionType::All,
1272 Query::select()
1273 .expr_as(Expr::col(CratesIoIden::OriginalName), Alias::new("name"))
1274 .expr_as(Expr::col(CratesIoIden::MaxVersion), Alias::new("version"))
1275 .expr_as(Expr::col(CratesIoIden::LastModified), Alias::new("date"))
1276 .expr_as(
1277 Expr::col(CratesIoIden::TotalDownloads),
1278 Alias::new("total_downloads"),
1279 )
1280 .expr_as(
1281 Expr::col(CratesIoIden::Description),
1282 Alias::new("description"),
1283 )
1284 .expr_as(
1285 Expr::col(CratesIoMetaIden::Documentation),
1286 Alias::new("documentation"),
1287 )
1288 .expr_as(Expr::cust("true"), Alias::new("is_cache"))
1289 .from(CratesIoMetaIden::Table)
1290 .inner_join(
1291 CratesIoIden::Table,
1292 Expr::col((CratesIoMetaIden::Table, CratesIoMetaIden::CratesIoFk))
1293 .equals((CratesIoIden::Table, CratesIoIden::Id)),
1294 )
1295 .and_where(
1296 Expr::col((CratesIoMetaIden::Table, CratesIoMetaIden::Version))
1297 .equals((CratesIoIden::Table, CratesIoIden::MaxVersion)),
1298 )
1299 .to_owned(),
1300 )
1301 .order_by(Alias::new("name"), Order::Asc)
1302 .limit(limit)
1303 .offset(offset)
1304 .to_owned()
1305 };
1306
1307 let builder = self.db_con.get_database_backend();
1308 let result = CrateOverview::find_by_statement(builder.build(&stmt))
1309 .all(&self.db_con)
1310 .await?;
1311
1312 Ok(result)
1313 }
1314
1315 async fn get_crate_data(&self, crate_name: &NormalizedName) -> DbResult<CrateData> {
1316 let krate = krate::Entity::find()
1317 .filter(krate::Column::Name.eq(crate_name.to_string()))
1318 .one(&self.db_con)
1319 .await?
1320 .ok_or_else(|| DbError::CrateNotFound(crate_name.to_string()))?;
1321
1322 let owners: Vec<String> = krate
1323 .find_related(owner::Entity)
1324 .find_also_related(user::Entity)
1325 .all(&self.db_con)
1326 .await?
1327 .into_iter()
1328 .filter_map(|(_, v)| v.map(|v| v.name))
1329 .collect();
1330 let categories: Vec<String> = krate
1331 .find_related(crate_category_to_crate::Entity)
1332 .find_also_related(crate_category::Entity)
1333 .all(&self.db_con)
1334 .await?
1335 .into_iter()
1336 .filter_map(|(_, v)| v.map(|v| v.category))
1337 .collect();
1338 let keywords: Vec<String> = krate
1339 .find_related(crate_keyword_to_crate::Entity)
1340 .find_also_related(crate_keyword::Entity)
1341 .all(&self.db_con)
1342 .await?
1343 .into_iter()
1344 .filter_map(|(_, v)| v.map(|v| v.keyword))
1345 .collect();
1346 let authors: Vec<String> = krate
1347 .find_related(crate_author_to_crate::Entity)
1348 .find_also_related(crate_author::Entity)
1349 .all(&self.db_con)
1350 .await?
1351 .into_iter()
1352 .filter_map(|(_, v)| v.map(|v| v.author))
1353 .collect();
1354 let crate_metas = krate
1355 .find_related(crate_meta::Entity)
1356 .all(&self.db_con)
1357 .await?;
1358 let crate_indices = krate
1359 .find_related(crate_index::Entity)
1360 .all(&self.db_con)
1361 .await?;
1362
1363 let mut versions = Vec::new();
1364 for cm in crate_metas {
1365 let ci = crate_indices
1366 .iter()
1367 .find(|ci| ci.vers == cm.version)
1368 .ok_or(DbError::CrateIndexNotFound(
1369 krate.name.clone(),
1370 cm.version.clone(),
1371 ))?;
1372 let dependencies: Vec<CrateRegistryDep> = match ci.deps.clone() {
1373 Some(deps) => {
1374 let ix = serde_json::from_value::<Vec<IndexDep>>(deps)
1375 .map_err(|e| DbError::FailedToConvertToJson(e.to_string()))?;
1376
1377 let mut ft = Vec::new();
1378 for dep in ix {
1379 ft.push(CrateRegistryDep::from_index(
1380 get_desc_for_crate_dep(
1381 &self.db_con,
1382 &dep.name,
1383 dep.registry.as_deref(),
1384 )
1385 .await?,
1386 dep,
1387 ));
1388 }
1389
1390 ft
1391 }
1392 None => Vec::default(),
1393 };
1394 let features: BTreeMap<String, Vec<String>> = match ci.features.clone() {
1395 Some(features) => serde_json::from_value::<BTreeMap<String, Vec<String>>>(features)
1396 .map_err(|e| DbError::FailedToConvertToJson(e.to_string()))?,
1397 None => BTreeMap::default(),
1398 };
1399
1400 versions.push(CrateVersionData {
1401 version: cm.version,
1402 created: cm.created,
1403 downloads: cm.downloads,
1404 readme: cm.readme,
1405 license: cm.license,
1406 license_file: cm.license_file,
1407 documentation: cm.documentation,
1408 dependencies,
1409 checksum: ci.cksum.clone(),
1410 features,
1411 yanked: ci.yanked,
1412 links: ci.links.clone(),
1413 v: ci.v,
1414 });
1415 }
1416 versions.sort_by(|a, b| {
1417 Version::from_unchecked_str(&b.version).cmp(&Version::from_unchecked_str(&a.version))
1418 });
1419
1420 let crate_data = CrateData {
1421 name: krate.original_name,
1422 owners,
1423 max_version: krate.max_version,
1424 total_downloads: krate.total_downloads,
1425 last_updated: krate.last_updated,
1426 homepage: krate.homepage,
1427 description: krate.description,
1428 repository: krate.repository,
1429 categories,
1430 keywords,
1431 authors,
1432 versions,
1433 };
1434
1435 Ok(crate_data)
1436 }
1437
1438 async fn add_empty_crate(&self, name: &str, created: &DateTime<Utc>) -> DbResult<i64> {
1439 let created = created.format(DB_DATE_FORMAT).to_string();
1440 let normalized_name = NormalizedName::from(
1441 OriginalName::try_from(name)
1442 .map_err(|_| DbError::InvalidCrateName(name.to_string()))?,
1443 );
1444 let krate = krate::ActiveModel {
1445 id: ActiveValue::default(),
1446 name: Set(normalized_name.to_string()),
1447 original_name: Set(name.to_string()),
1448 max_version: Set("0.0.0".to_string()),
1449 last_updated: Set(created.clone()),
1450 total_downloads: Set(0),
1451 homepage: Set(None),
1452 description: Set(None),
1453 repository: Set(None),
1454 e_tag: Set(String::new()), restricted_download: Set(false),
1456 };
1457 Ok(krate.insert(&self.db_con).await?.id)
1458 }
1459
1460 async fn add_crate(
1461 &self,
1462 pub_metadata: &PublishMetadata,
1463 cksum: &str,
1464 created: &DateTime<Utc>,
1465 owner: &str,
1466 ) -> DbResult<i64> {
1467 let created = created.format(DB_DATE_FORMAT).to_string();
1468 let normalized_name = NormalizedName::from(
1469 OriginalName::try_from(&pub_metadata.name)
1470 .map_err(|_| DbError::InvalidCrateName(pub_metadata.name.clone()))?,
1471 );
1472
1473 let existing = krate::Entity::find()
1474 .filter(krate::Column::Name.eq(pub_metadata.name.clone()))
1475 .one(&self.db_con)
1476 .await?;
1477
1478 let txn = self.db_con.begin().await?;
1479
1480 let crate_id = if let Some(krate) = existing {
1481 let krate_id = krate.id;
1482 let current_max_version = Version::try_from(&krate.max_version)
1483 .map_err(|_| DbError::InvalidVersion(krate.max_version.clone()))?;
1484 let max_version = current_max_version.max(
1485 Version::try_from(&pub_metadata.vers)
1486 .map_err(|_| DbError::InvalidVersion(pub_metadata.vers.clone()))?,
1487 );
1488
1489 let mut krate: krate::ActiveModel = krate.into();
1490 krate.last_updated = Set(created.clone());
1491 krate.max_version = Set(max_version.to_string());
1492 krate.homepage = Set(pub_metadata.homepage.clone());
1493 krate.description = Set(pub_metadata.description.clone());
1494 krate.repository = Set(pub_metadata.repository.clone());
1495 krate.e_tag = Set(String::new()); krate.update(&txn).await?;
1497 krate_id
1498 } else {
1499 let krate = krate::ActiveModel {
1500 id: ActiveValue::default(),
1501 name: Set(normalized_name.to_string()),
1502 original_name: Set(pub_metadata.name.clone()),
1503 max_version: Set(pub_metadata.vers.clone()),
1504 last_updated: Set(created.clone()),
1505 total_downloads: Set(0),
1506 homepage: Set(pub_metadata.homepage.clone()),
1507 description: Set(pub_metadata.description.clone()),
1508 repository: Set(pub_metadata.repository.clone()),
1509 e_tag: Set(String::new()), restricted_download: Set(false),
1511 };
1512 let krate = krate.insert(&txn).await?;
1513 krate.id
1514 };
1515
1516 add_owner_if_not_exists(&txn, owner, crate_id).await?;
1517 add_crate_metadata(&txn, pub_metadata, &created, crate_id).await?;
1518 add_crate_index(&txn, pub_metadata, cksum, crate_id).await?;
1519 update_etag(&txn, &pub_metadata.name, crate_id).await?;
1520 update_crate_categories(&txn, pub_metadata, crate_id).await?;
1521 update_crate_keywords(&txn, pub_metadata, crate_id).await?;
1522 update_crate_authors(&txn, pub_metadata, crate_id).await?;
1523
1524 txn.commit().await?;
1525 Ok(crate_id)
1526 }
1527
1528 async fn update_docs_link(
1529 &self,
1530 crate_name: &NormalizedName,
1531 version: &Version,
1532 docs_link: &str,
1533 ) -> DbResult<()> {
1534 let (cm, _c) = crate_meta::Entity::find()
1535 .find_also_related(krate::Entity)
1536 .filter(
1537 Cond::all()
1538 .add(krate::Column::Name.eq(crate_name.to_string()))
1539 .add(crate_meta::Column::Version.eq(version.to_string())),
1540 )
1541 .one(&self.db_con)
1542 .await?
1543 .ok_or(DbError::CrateNotFound(crate_name.to_string()))?;
1544
1545 let mut cm: crate_meta::ActiveModel = cm.into();
1546 cm.documentation = Set(Some(docs_link.to_string()));
1547 cm.update(&self.db_con).await?;
1548 Ok(())
1549 }
1550
1551 async fn add_crate_metadata(
1552 &self,
1553 pub_metadata: &PublishMetadata,
1554 created: &str,
1555 crate_id: i64,
1556 ) -> DbResult<()> {
1557 add_crate_metadata(&self.db_con, pub_metadata, created, crate_id).await
1558 }
1559
1560 async fn get_prefetch_data(&self, crate_name: &str) -> DbResult<Prefetch> {
1561 let krate = krate::Entity::find()
1562 .filter(krate::Column::Name.eq(crate_name))
1563 .find_with_related(crate_index::Entity)
1564 .all(&self.db_con)
1565 .await?;
1566
1567 if krate.len() != 1 {
1569 return Err(DbError::CrateNotFound(crate_name.to_string()));
1570 }
1571
1572 let (krate, crate_indices) = krate[0].to_owned();
1573 let index_metadata = crate_index_model_to_index_metadata(crate_name, crate_indices)?;
1574 let data = index_metadata_to_bytes(&index_metadata)?;
1575
1576 Ok(Prefetch {
1577 data,
1578 etag: krate.e_tag.clone(),
1579 last_modified: krate.last_updated,
1580 })
1581 }
1582
1583 async fn is_cratesio_cache_up_to_date(
1584 &self,
1585 crate_name: &NormalizedName,
1586 etag: Option<String>,
1587 last_modified: Option<String>,
1588 ) -> DbResult<PrefetchState> {
1589 let Some(krate) = cratesio_crate::Entity::find()
1590 .filter(cratesio_crate::Column::Name.eq(crate_name.to_string()))
1591 .one(&self.db_con)
1592 .await?
1593 else {
1594 return Ok(PrefetchState::NotFound);
1595 };
1596
1597 let needs_update = match (etag, last_modified) {
1598 (Some(etag), Some(last_modified)) => {
1599 krate.e_tag != etag || krate.last_modified != last_modified
1600 }
1601 (Some(etag), None) => krate.e_tag != etag,
1602 (None, Some(last_modified)) => krate.last_modified != last_modified,
1603 (None, None) => true,
1604 };
1605
1606 if !needs_update {
1607 Ok(PrefetchState::UpToDate)
1608 } else {
1609 let crate_indices = krate
1610 .find_related(cratesio_index::Entity)
1611 .all(&self.db_con)
1612 .await?;
1613 let index_metadata = cratesio_index_model_to_index_metadata(crate_name, crate_indices)?;
1614 let data = index_metadata_to_bytes(&index_metadata)?;
1615
1616 Ok(PrefetchState::NeedsUpdate(Prefetch {
1617 data,
1618 etag: krate.e_tag.clone(),
1619 last_modified: krate.last_modified,
1620 }))
1621 }
1622 }
1623
1624 async fn add_cratesio_prefetch_data(
1625 &self,
1626 crate_name: &OriginalName,
1627 etag: &str,
1628 last_modified: &str,
1629 description: Option<String>,
1630 indices: &[IndexMetadata],
1631 ) -> DbResult<Prefetch> {
1632 let normalized_name = crate_name.to_normalized();
1633
1634 let max_version = indices
1635 .iter()
1636 .map(|i| Version::from_unchecked_str(&i.vers))
1637 .max()
1638 .ok_or(DbError::FailedToGetMaxVersionByName(crate_name.to_string()))?;
1639
1640 let krate = cratesio_crate::Entity::find()
1641 .filter(cratesio_crate::Column::Name.eq(normalized_name.to_string()))
1642 .one(&self.db_con)
1643 .await?;
1644
1645 let krate = if let Some(krate) = krate {
1646 let mut krate: cratesio_crate::ActiveModel = krate.into();
1647 krate.e_tag = Set(etag.to_string());
1648 krate.last_modified = Set(last_modified.to_string());
1649 krate.max_version = Set(max_version.to_string());
1650 krate.update(&self.db_con).await?
1651 } else {
1652 let krate = cratesio_crate::ActiveModel {
1653 id: ActiveValue::default(),
1654 name: Set(normalized_name.to_string()),
1655 original_name: Set(crate_name.to_string()),
1656 description: Set(description),
1657 e_tag: Set(etag.to_string()),
1658 last_modified: Set(last_modified.to_string()),
1659 total_downloads: Set(0),
1660 max_version: Set(max_version.to_string()),
1661 };
1662 krate.insert(&self.db_con).await?
1663 };
1664
1665 let current_indices = cratesio_index::Entity::find()
1666 .filter(cratesio_index::Column::CratesIoFk.eq(krate.id))
1667 .all(&self.db_con)
1668 .await?;
1669
1670 for index in indices {
1671 if let Some(current_index) = current_indices.iter().find(|ci| index.vers == ci.vers) {
1673 if index.yanked != current_index.yanked {
1674 let mut ci: cratesio_index::ActiveModel = current_index.to_owned().into();
1675 ci.yanked = Set(index.yanked);
1676 ci.update(&self.db_con).await?;
1677 }
1678 } else {
1679 let deps = if index.deps.is_empty() {
1680 None
1681 } else {
1682 let deps = serde_json::to_value(&index.deps)
1683 .map_err(|e| DbError::FailedToConvertToJson(e.to_string()))?;
1684 Some(deps)
1685 };
1686
1687 let features = serde_json::to_value(&index.features)
1688 .map_err(|e| DbError::FailedToConvertToJson(e.to_string()))?;
1689
1690 let features2 = serde_json::to_value(&index.features2)
1691 .map_err(|e| DbError::FailedToConvertToJson(e.to_string()))?;
1692
1693 let new_index = cratesio_index::ActiveModel {
1694 id: ActiveValue::default(),
1695 name: Set(index.name.clone()),
1696 vers: Set(index.vers.clone()),
1697 deps: Set(deps),
1698 cksum: Set(index.cksum.clone()),
1699 features: Set(Some(features)),
1700 features2: Set(Some(features2)),
1701 yanked: Set(index.yanked),
1702 links: Set(index.links.clone()),
1703 v: Set(index.v.unwrap_or(1) as i32),
1704 crates_io_fk: Set(krate.id),
1705 };
1706
1707 new_index.insert(&self.db_con).await?;
1708
1709 let meta = cratesio_meta::ActiveModel {
1711 id: ActiveValue::default(),
1712 version: Set(index.vers.clone()),
1713 downloads: Set(0),
1714 crates_io_fk: Set(krate.id),
1715 documentation: Set(Some(format!(
1716 "https://docs.rs/{normalized_name}/{}",
1717 index.vers,
1718 ))),
1719 };
1720
1721 meta.insert(&self.db_con).await?;
1722 }
1723 }
1724
1725 Ok(Prefetch {
1726 data: index_metadata_to_bytes(indices)?,
1727 etag: etag.to_string(),
1728 last_modified: last_modified.to_string(),
1729 })
1730 }
1731
1732 async fn get_cratesio_index_update_list(&self) -> DbResult<Vec<CratesioPrefetchMsg>> {
1733 let crates = cratesio_crate::Entity::find().all(&self.db_con).await?;
1734 let msgs = crates
1735 .into_iter()
1736 .map(|krate| {
1737 CratesioPrefetchMsg::Update(UpdateData {
1738 name: OriginalName::from_unchecked(krate.original_name),
1739 etag: Some(krate.e_tag),
1740 last_modified: Some(krate.last_modified),
1741 })
1742 })
1743 .collect();
1744 Ok(msgs)
1745 }
1746
1747 async fn unyank_crate(&self, crate_name: &NormalizedName, version: &Version) -> DbResult<()> {
1748 let ci = crate_index::Entity::find()
1749 .filter(crate_index::Column::Name.eq(crate_name.to_string()))
1750 .filter(crate_index::Column::Vers.eq(version.to_string()))
1751 .one(&self.db_con)
1752 .await?;
1753
1754 let mut ci: crate_index::ActiveModel = ci
1755 .ok_or(DbError::CrateIndexNotFound(
1756 crate_name.to_string(),
1757 version.to_string(),
1758 ))?
1759 .into();
1760
1761 ci.yanked = Set(false);
1762 ci.save(&self.db_con).await?;
1763
1764 Ok(())
1765 }
1766
1767 async fn yank_crate(&self, crate_name: &NormalizedName, version: &Version) -> DbResult<()> {
1768 let ci = crate_index::Entity::find()
1769 .filter(crate_index::Column::Name.eq(crate_name.to_string()))
1770 .filter(crate_index::Column::Vers.eq(version.to_string()))
1771 .one(&self.db_con)
1772 .await?;
1773
1774 let mut ci: crate_index::ActiveModel = ci
1775 .ok_or(DbError::CrateIndexNotFound(
1776 crate_name.to_string(),
1777 version.to_string(),
1778 ))?
1779 .into();
1780
1781 ci.yanked = Set(true);
1782 ci.save(&self.db_con).await?;
1783
1784 Ok(())
1785 }
1786
1787 async fn register_webhook(&self, webhook: Webhook) -> DbResult<String> {
1788 let w = webhook::ActiveModel {
1789 event: Set(Into::<&str>::into(webhook.event).to_string()),
1790 callback_url: Set(webhook.callback_url),
1791 name: Set(webhook.name),
1792 ..Default::default()
1793 };
1794
1795 let w: webhook::Model = w.insert(&self.db_con).await?;
1796 Ok(w.id.to_string())
1797 }
1798 async fn delete_webhook(&self, id: &str) -> DbResult<()> {
1799 let w = webhook::Entity::find()
1800 .filter(webhook::Column::Id.eq(
1801 TryInto::<Uuid>::try_into(id).map_err(|_| DbError::InvalidId(id.to_string()))?,
1802 ))
1803 .one(&self.db_con)
1804 .await?
1805 .ok_or(DbError::WebhookNotFound)?;
1806
1807 w.delete(&self.db_con).await?;
1808 Ok(())
1809 }
1810 async fn get_webhook(&self, id: &str) -> DbResult<Webhook> {
1811 let w = webhook::Entity::find()
1812 .filter(webhook::Column::Id.eq(
1813 TryInto::<Uuid>::try_into(id).map_err(|_| DbError::InvalidId(id.to_string()))?,
1814 ))
1815 .one(&self.db_con)
1816 .await?
1817 .ok_or(DbError::WebhookNotFound)?;
1818
1819 Ok(Webhook {
1820 id: Some(w.id.into()),
1821 name: w.name,
1822 event: w
1823 .event
1824 .as_str()
1825 .try_into()
1826 .map_err(|_| DbError::InvalidWebhookEvent(w.event))?,
1827 callback_url: w.callback_url,
1828 })
1829 }
1830 async fn get_all_webhooks(&self) -> DbResult<Vec<Webhook>> {
1831 let w = webhook::Entity::find().all(&self.db_con).await?;
1832
1833 Ok(w.into_iter()
1834 .filter_map(|w| {
1835 Some(Webhook {
1836 id: Some(w.id.into()),
1837 name: w.name,
1838 event: w.event.as_str().try_into().ok()?,
1840 callback_url: w.callback_url,
1841 })
1842 })
1843 .collect())
1844 }
1845 async fn add_webhook_queue(
1846 &self,
1847 event: WebhookEvent,
1848 payload: serde_json::Value,
1849 ) -> DbResult<()> {
1850 let w = webhook::Entity::find()
1851 .filter(webhook::Column::Event.eq(Into::<&str>::into(event)))
1852 .all(&self.db_con)
1853 .await?;
1854
1855 if w.is_empty() {
1856 return Ok(());
1857 }
1858
1859 let now = Utc::now();
1860
1861 let entries = w.iter().map(|w| webhook_queue::ActiveModel {
1862 webhook_fk: Set(w.id),
1863 payload: Set(payload.clone()),
1864 next_attempt: Set(now.into()),
1865 last_attempt: Set(None),
1866 ..Default::default()
1867 });
1868
1869 webhook_queue::Entity::insert_many(entries)
1870 .exec(&self.db_con)
1871 .await?;
1872 Ok(())
1873 }
1874 async fn get_pending_webhook_queue_entries(
1875 &self,
1876 timestamp: DateTime<Utc>,
1877 ) -> DbResult<Vec<WebhookQueue>> {
1878 let w = webhook_queue::Entity::find()
1879 .find_with_related(webhook::Entity)
1880 .filter(webhook_queue::Column::NextAttempt.lte(timestamp))
1881 .all(&self.db_con)
1882 .await?;
1883
1884 Ok(w.iter()
1885 .filter_map(|w| {
1886 Some(WebhookQueue {
1887 id: Into::<String>::into(w.0.id),
1888 callback_url: w.1.first()?.callback_url.clone(),
1889 payload: w.0.payload.clone(),
1890 last_attempt: w.0.last_attempt.map(Into::into),
1891 next_attempt: w.0.next_attempt.into(),
1892 })
1893 })
1894 .collect())
1895 }
1896 async fn update_webhook_queue(
1897 &self,
1898 id: &str,
1899 last_attempt: DateTime<Utc>,
1900 next_attempt: DateTime<Utc>,
1901 ) -> DbResult<()> {
1902 let w = webhook_queue::Entity::find()
1903 .filter(webhook_queue::Column::Id.eq(
1904 TryInto::<Uuid>::try_into(id).map_err(|_| DbError::InvalidId(id.to_string()))?,
1905 ))
1906 .one(&self.db_con)
1907 .await?
1908 .ok_or(DbError::WebhookNotFound)?;
1909
1910 let mut w: webhook_queue::ActiveModel = w.into();
1911 w.last_attempt = Set(Some(last_attempt.into()));
1912 w.next_attempt = Set(next_attempt.into());
1913 w.update(&self.db_con).await?;
1914 Ok(())
1915 }
1916 async fn delete_webhook_queue(&self, id: &str) -> 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 w.delete(&self.db_con).await?;
1926 Ok(())
1927 }
1928}
1929
1930async fn get_desc_for_crate_dep<C: ConnectionTrait>(
1933 db_con: &C,
1934 name: &str,
1935 registry: Option<&str>,
1936) -> DbResult<Option<String>> {
1937 let desc = if registry.unwrap_or_default() == "https://github.com/rust-lang/crates.io-index" {
1938 let krate = cratesio_crate::Entity::find()
1939 .filter(cratesio_crate::Column::Name.eq(name))
1940 .one(db_con)
1941 .await?;
1942 krate.and_then(|krate| krate.description)
1943 } else {
1944 let krate = krate::Entity::find()
1947 .filter(krate::Column::Name.eq(name))
1948 .one(db_con)
1949 .await?;
1950 krate.and_then(|krate| krate.description)
1951 };
1952
1953 Ok(desc)
1954}
1955
1956async fn insert_admin_credentials<C: ConnectionTrait>(
1957 db_con: &C,
1958 con_string: &ConString,
1959) -> DbResult<()> {
1960 let hashed_pwd = hash_pwd(&con_string.admin_pwd(), &con_string.salt());
1961
1962 let admin = user::ActiveModel {
1963 name: Set("admin".to_string()),
1964 pwd: Set(hashed_pwd),
1965 salt: Set(con_string.salt()),
1966 is_admin: Set(true),
1967 is_read_only: Set(false),
1968 ..Default::default()
1969 };
1970
1971 let res: InsertResult<user::ActiveModel> = user::Entity::insert(admin).exec(db_con).await?;
1972 let auth_token = hash_token(&con_string.admin_token());
1973
1974 let auth_token = auth_token::ActiveModel {
1975 name: Set("admin".to_string()),
1976 token: Set(auth_token),
1977 user_fk: Set(res.last_insert_id),
1978 ..Default::default()
1979 };
1980 auth_token::Entity::insert(auth_token).exec(db_con).await?;
1981
1982 Ok(())
1983}
1984
1985async fn no_user_exists<C: ConnectionTrait>(db_con: &C) -> DbResult<bool> {
1986 let id = user::Entity::find()
1987 .one(db_con)
1988 .await?
1989 .map(|model| model.id);
1990
1991 Ok(id.is_none())
1992}
1993
1994async fn add_owner_if_not_exists<C: ConnectionTrait>(
1995 db_con: &C,
1996 owner: &str,
1997 crate_id: i64,
1998) -> DbResult<()> {
1999 let user_fk = user::Entity::find()
2000 .filter(user::Column::Name.eq(owner))
2001 .one(db_con)
2002 .await?
2003 .map(|model| model.id)
2004 .ok_or_else(|| DbError::UserNotFound(owner.to_string()))?;
2005
2006 let owner = owner::Entity::find()
2007 .filter(owner::Column::CrateFk.eq(crate_id))
2008 .filter(owner::Column::UserFk.eq(user_fk))
2009 .one(db_con)
2010 .await?;
2011
2012 if owner.is_none() {
2013 let o = owner::ActiveModel {
2014 user_fk: Set(user_fk),
2015 crate_fk: Set(crate_id),
2016 ..Default::default()
2017 };
2018
2019 o.insert(db_con).await?;
2020 }
2021 Ok(())
2022}
2023
2024async fn add_crate_index<C: ConnectionTrait>(
2025 db_con: &C,
2026 pub_metadata: &PublishMetadata,
2027 cksum: &str,
2028 crate_id: i64,
2029) -> DbResult<()> {
2030 let index_data = IndexMetadata::from_reg_meta(pub_metadata, cksum);
2031
2032 let deps = if index_data.deps.is_empty() {
2033 None
2034 } else {
2035 let deps = serde_json::to_value(&index_data.deps)
2036 .map_err(|e| DbError::FailedToConvertToJson(e.to_string()))?;
2037 Some(deps)
2038 };
2039
2040 let features = serde_json::to_value(&index_data.features)
2041 .map_err(|e| DbError::FailedToConvertToJson(e.to_string()))?;
2042
2043 let ci = crate_index::ActiveModel {
2044 id: ActiveValue::default(),
2045 name: Set(index_data.name),
2046 vers: Set(index_data.vers),
2047 deps: Set(deps),
2048 cksum: Set(cksum.to_owned()),
2049 features: Set(Some(features)),
2050 yanked: ActiveValue::default(),
2051 links: Set(index_data.links),
2052 v: Set(index_data.v.unwrap_or(1) as i32),
2053 crate_fk: Set(crate_id),
2054 };
2055
2056 ci.insert(db_con).await?;
2057 Ok(())
2058}
2059
2060async fn add_crate_metadata<C: ConnectionTrait>(
2061 db_con: &C,
2062 pub_metadata: &PublishMetadata,
2063 created: &str,
2064 crate_id: i64,
2065) -> DbResult<()> {
2066 let cm = crate_meta::ActiveModel {
2067 id: ActiveValue::default(),
2068 version: Set(pub_metadata.vers.clone()),
2069 created: Set(created.to_string()),
2070 downloads: Set(0),
2071 crate_fk: Set(crate_id),
2072 readme: Set(pub_metadata.readme.clone()),
2073 license: Set(pub_metadata.license.clone()),
2074 license_file: Set(pub_metadata.license_file.clone()),
2075 documentation: Set(pub_metadata.documentation.clone()),
2076 };
2077
2078 cm.insert(db_con).await?;
2079
2080 Ok(())
2081}
2082
2083async fn update_crate_categories<C: ConnectionTrait>(
2084 db_con: &C,
2085 pub_metadata: &PublishMetadata,
2086 crate_id: i64,
2087) -> DbResult<()> {
2088 let categories = pub_metadata.categories.clone();
2089
2090 crate_category_to_crate::Entity::delete_many()
2092 .filter(crate_category_to_crate::Column::CrateFk.eq(crate_id))
2093 .exec(db_con)
2094 .await?;
2095
2096 for category in categories {
2098 let category_fk = crate_category::Entity::find()
2099 .filter(crate_category::Column::Category.eq(category.clone()))
2100 .one(db_con)
2101 .await?
2102 .map(|model| model.id);
2103
2104 let category_fk = if let Some(category_fk) = category_fk {
2106 category_fk
2107 } else {
2108 let cc = crate_category::ActiveModel {
2109 id: ActiveValue::default(),
2110 category: Set(category.clone()),
2111 };
2112
2113 cc.insert(db_con).await?.id
2114 };
2115
2116 let cctc = crate_category_to_crate::ActiveModel {
2118 id: ActiveValue::default(),
2119 crate_fk: Set(crate_id),
2120 category_fk: Set(category_fk),
2121 };
2122 cctc.insert(db_con).await?;
2123 }
2124
2125 Ok(())
2126}
2127
2128async fn update_crate_keywords<C: ConnectionTrait>(
2129 db_con: &C,
2130 pub_metadata: &PublishMetadata,
2131 crate_id: i64,
2132) -> DbResult<()> {
2133 let keywords = pub_metadata.keywords.clone();
2134
2135 crate_keyword_to_crate::Entity::delete_many()
2137 .filter(crate_keyword_to_crate::Column::CrateFk.eq(crate_id))
2138 .exec(db_con)
2139 .await?;
2140
2141 for keyword in keywords {
2143 let keyword_fk = crate_keyword::Entity::find()
2144 .filter(crate_keyword::Column::Keyword.eq(keyword.clone()))
2145 .one(db_con)
2146 .await?
2147 .map(|model| model.id);
2148
2149 let keyword_fk = if let Some(keyword_fk) = keyword_fk {
2151 keyword_fk
2152 } else {
2153 let ck = crate_keyword::ActiveModel {
2154 id: ActiveValue::default(),
2155 keyword: Set(keyword.clone()),
2156 };
2157
2158 ck.insert(db_con).await?.id
2159 };
2160
2161 let cktc = crate_keyword_to_crate::ActiveModel {
2163 id: ActiveValue::default(),
2164 crate_fk: Set(crate_id),
2165 keyword_fk: Set(keyword_fk),
2166 };
2167 cktc.insert(db_con).await?;
2168 }
2169
2170 Ok(())
2171}
2172
2173async fn update_crate_authors<C: ConnectionTrait>(
2174 db_con: &C,
2175 pub_metadata: &PublishMetadata,
2176 crate_id: i64,
2177) -> DbResult<()> {
2178 let authors = pub_metadata.authors.clone().unwrap_or_default();
2179
2180 crate_author_to_crate::Entity::delete_many()
2182 .filter(crate_author_to_crate::Column::CrateFk.eq(crate_id))
2183 .exec(db_con)
2184 .await?;
2185
2186 for author in authors {
2188 let author_fk = crate_author::Entity::find()
2189 .filter(crate_author::Column::Author.eq(author.clone()))
2190 .one(db_con)
2191 .await?
2192 .map(|model| model.id);
2193
2194 let author_fk = if let Some(author_fk) = author_fk {
2196 author_fk
2197 } else {
2198 let ca = crate_author::ActiveModel {
2199 id: ActiveValue::default(),
2200 author: Set(author.clone()),
2201 };
2202
2203 ca.insert(db_con).await?.id
2204 };
2205
2206 let catc = crate_author_to_crate::ActiveModel {
2208 id: ActiveValue::default(),
2209 crate_fk: Set(crate_id),
2210 author_fk: Set(author_fk),
2211 };
2212 catc.insert(db_con).await?;
2213 }
2214
2215 Ok(())
2216}
2217
2218async fn compute_etag<C: ConnectionTrait>(
2219 db_con: &C,
2220 crate_name: &str,
2221 crate_id: i64,
2222) -> DbResult<String> {
2223 let crate_indices = crate_index::Entity::find()
2224 .filter(crate_index::Column::CrateFk.eq(crate_id))
2225 .all(db_con)
2226 .await?;
2227
2228 let index_metadata = crate_index_model_to_index_metadata(crate_name, crate_indices)?;
2229 let data = index_metadata_to_bytes(&index_metadata)?;
2230
2231 Ok(sha256::digest(data))
2232}
2233
2234fn index_metadata_to_bytes(index_metadata: &[IndexMetadata]) -> DbResult<Vec<u8>> {
2235 IndexMetadata::serialize_indices(index_metadata)
2236 .map(String::into_bytes)
2237 .map_err(|e| DbError::FailedToConvertToJson(format!("{e}")))
2238}
2239
2240fn crate_index_model_to_index_metadata(
2241 crate_name: &str,
2242 crate_indices: Vec<crate_index::Model>,
2243) -> DbResult<Vec<IndexMetadata>> {
2244 let mut index_metadata = vec![];
2245 for ci in crate_indices {
2246 let deps = match ci.deps {
2247 Some(ref deps) => serde_json::value::from_value(deps.to_owned()).map_err(|e| {
2248 DbError::FailedToConvertFromJson(format!(
2249 "Failed to deserialize crate dependencies of {crate_name}: {e}"
2250 ))
2251 })?,
2252 None => vec![],
2253 };
2254 let features = ci.features.clone().unwrap_or_default();
2255 let features = serde_json::value::from_value(features).map_err(|e| {
2256 DbError::FailedToConvertFromJson(format!(
2257 "Failed to deserialize crate features of {crate_name}: {e}"
2258 ))
2259 })?;
2260
2261 let cm = IndexMetadata {
2262 name: ci.name,
2263 vers: ci.vers,
2264 deps,
2265 cksum: ci.cksum,
2266 features,
2267 yanked: ci.yanked,
2268 links: ci.links,
2269 v: Some(ci.v as u32),
2270 features2: None,
2271 };
2272 index_metadata.push(cm);
2273 }
2274 Ok(index_metadata)
2275}
2276
2277fn cratesio_index_model_to_index_metadata(
2278 crate_name: &NormalizedName,
2279 crate_indices: Vec<cratesio_index::Model>,
2280) -> DbResult<Vec<IndexMetadata>> {
2281 let mut index_metadata = vec![];
2282 for ci in crate_indices {
2283 let deps = match ci.deps {
2284 Some(ref deps) => serde_json::value::from_value(deps.to_owned()).map_err(|e| {
2285 DbError::FailedToConvertFromJson(format!(
2286 "Failed to deserialize crate dependencies of {crate_name}: {e}"
2287 ))
2288 })?,
2289 None => vec![],
2290 };
2291 let features = ci.features.clone().unwrap_or_default();
2292 let features = serde_json::value::from_value(features).map_err(|e| {
2293 DbError::FailedToConvertFromJson(format!(
2294 "Failed to deserialize crate features of {crate_name}: {e}"
2295 ))
2296 })?;
2297
2298 let features2 = ci.features2.clone().unwrap_or_default();
2299 let features2 = serde_json::value::from_value(features2).map_err(|e| {
2300 DbError::FailedToConvertFromJson(format!(
2301 "Failed to deserialize crate features of {crate_name}: {e}"
2302 ))
2303 })?;
2304
2305 let cm = IndexMetadata {
2306 name: ci.name,
2307 vers: ci.vers.clone(),
2308 deps,
2309 cksum: ci.cksum.clone(),
2310 features,
2311 features2,
2312 yanked: ci.yanked,
2313 links: ci.links.clone(),
2314 v: Some(ci.v as u32),
2315 };
2316 index_metadata.push(cm);
2317 }
2318 Ok(index_metadata)
2319}
2320
2321async fn update_etag<C: ConnectionTrait>(
2322 db_con: &C,
2323 crate_name: &str,
2324 crate_id: i64,
2325) -> DbResult<()> {
2326 let etag = compute_etag(db_con, crate_name, crate_id).await?;
2327 let krate = krate::Entity::find()
2328 .filter(krate::Column::Id.eq(crate_id))
2329 .one(db_con)
2330 .await?
2331 .ok_or(DbError::CrateNotFound(crate_name.to_string()))?;
2332 let mut krate: krate::ActiveModel = krate.into();
2333 krate.e_tag = Set(etag);
2334 krate.update(db_con).await?;
2335 Ok(())
2336}
2337
2338async fn get_max_version_from_id<C: ConnectionTrait>(
2339 db_con: &C,
2340 crate_id: i64,
2341) -> DbResult<Version> {
2342 let krate = krate::Entity::find_by_id(crate_id).one(db_con).await?;
2343
2344 let k = krate.ok_or(DbError::FailedToGetMaxVersionById(crate_id))?;
2345 let v = Version::try_from(&k.max_version)
2346 .map_err(|_| DbError::FailedToGetMaxVersionById(crate_id))?;
2347 Ok(v)
2348}
2349
2350pub mod test_utils {
2353
2354 use super::*;
2355
2356 pub async fn test_add_cached_crate_with_downloads(
2357 db: &Database,
2358 name: &str,
2359 version: &str,
2360 downloads: u64,
2361 ) -> DbResult<()> {
2362 let _ = test_add_cached_crate(db, name, version).await?;
2363
2364 let krate = cratesio_crate::Entity::find()
2365 .filter(cratesio_crate::Column::Name.eq(name))
2366 .one(&db.db_con)
2367 .await?
2368 .ok_or(DbError::CrateNotFound(name.to_string()))?;
2369
2370 let total_downloads = krate.total_downloads as u64;
2371
2372 let mut krate: cratesio_crate::ActiveModel = krate.into();
2373 krate.total_downloads = Set((total_downloads + downloads) as i64);
2374 krate.update(&db.db_con).await?;
2375
2376 Ok(())
2377 }
2378
2379 pub async fn test_add_cached_crate(
2380 db: &Database,
2381 name: &str,
2382 version: &str,
2383 ) -> DbResult<Prefetch> {
2384 let etag = "etag";
2385 let last_modified = "last_modified";
2386 let description = Some(String::from("description"));
2387 let indices = vec![IndexMetadata {
2388 name: name.to_string(),
2389 vers: version.to_string(),
2390 deps: vec![],
2391 cksum: "cksum".to_string(),
2392 features: BTreeMap::new(),
2393 features2: None,
2394 yanked: false,
2395 links: None,
2396 v: Some(1),
2397 }];
2398
2399 db.add_cratesio_prefetch_data(
2400 &OriginalName::from_unchecked(name.to_string()),
2401 etag,
2402 last_modified,
2403 description,
2404 &indices,
2405 )
2406 .await
2407 }
2408
2409 pub async fn test_add_crate(
2410 db: &Database,
2411 name: &str,
2412 owner: &str,
2413 version: &Version,
2414 created: &DateTime<Utc>,
2415 ) -> DbResult<i64> {
2416 let pm = PublishMetadata {
2417 name: name.to_string(),
2418 vers: version.to_string(),
2419 ..PublishMetadata::default()
2420 };
2421 let user = user::Entity::find()
2422 .filter(user::Column::Name.eq(owner))
2423 .one(&db.db_con)
2424 .await?;
2425 if user.is_none() {
2426 db.add_user(name, "pwd", "salt", false, false).await?;
2427 }
2428
2429 db.add_crate(&pm, "cksum", created, owner).await
2430 }
2431
2432 pub async fn test_add_crate_with_downloads(
2433 db: &Database,
2434 name: &str,
2435 owner: &str,
2436 version: &Version,
2437 created: &DateTime<Utc>,
2438 downloads: Option<i64>,
2439 ) -> DbResult<i64> {
2440 let pm = PublishMetadata {
2441 name: name.to_string(),
2442 vers: version.to_string(),
2443 ..PublishMetadata::default()
2444 };
2445 let user = user::Entity::find()
2446 .filter(user::Column::Name.eq(owner))
2447 .one(&db.db_con)
2448 .await?;
2449 if user.is_none() {
2450 db.add_user(name, "pwd", "salt", false, false).await?;
2451 }
2452
2453 db.add_crate(&pm, "cksum", created, owner).await?;
2454 let (cm, krate) = crate_meta::Entity::find()
2455 .find_also_related(krate::Entity)
2456 .filter(krate::Column::Name.eq(name))
2457 .filter(crate_meta::Column::Version.eq(version.to_string()))
2458 .one(&db.db_con)
2459 .await?
2460 .ok_or(DbError::CrateNotFound(name.to_string()))?;
2461 let mut cm: crate_meta::ActiveModel = cm.into();
2462
2463 let current_downloads = krate.as_ref().unwrap().total_downloads;
2464 let crate_id = krate.as_ref().unwrap().id;
2465
2466 let mut krate: krate::ActiveModel = krate.unwrap().into();
2467 krate.total_downloads = Set(current_downloads + downloads.unwrap_or(0));
2468 krate.update(&db.db_con).await?;
2469 cm.downloads = Set(downloads.unwrap_or_default());
2470 cm.update(&db.db_con).await?;
2471 Ok(crate_id)
2472 }
2473
2474 pub async fn test_add_crate_meta(
2475 db: &Database,
2476 crate_id: i64,
2477 version: &str,
2478 created: &DateTime<Utc>,
2479 downloads: Option<i64>,
2480 ) -> DbResult<()> {
2481 let cm = crate_meta::ActiveModel {
2482 id: ActiveValue::default(),
2483 version: Set(version.to_string()),
2484 created: Set(created.to_string()),
2485 downloads: Set(downloads.unwrap_or_default()),
2486 crate_fk: Set(crate_id),
2487 ..Default::default()
2488 };
2489
2490 cm.insert(&db.db_con).await?;
2491
2492 Ok(())
2493 }
2494
2495 pub async fn test_delete_crate_index(db: &Database, crate_id: i64) -> DbResult<()> {
2496 crate_index::Entity::delete_many()
2497 .filter(crate_index::Column::CrateFk.eq(crate_id))
2498 .exec(&db.db_con)
2499 .await?;
2500 Ok(())
2501 }
2502
2503 pub async fn clean_db(db: &Database, session_age: std::time::Duration) -> DbResult<()> {
2504 let session_age = chrono::Duration::from_std(session_age).unwrap();
2505 let now = std::ops::Add::add(Utc::now(), session_age)
2506 .format(DB_DATE_FORMAT)
2507 .to_string();
2508
2509 session::Entity::delete_many()
2510 .filter(Expr::col(session::Column::Created).lt(now))
2511 .exec(&db.db_con)
2512 .await?;
2513
2514 Ok(())
2515 }
2516
2517 pub async fn get_crate_meta_list(db: &Database, crate_id: i64) -> DbResult<Vec<CrateMeta>> {
2518 let cm: Vec<(crate_meta::Model, Option<krate::Model>)> = crate_meta::Entity::find()
2519 .find_also_related(krate::Entity)
2520 .filter(crate_meta::Column::CrateFk.eq(crate_id))
2521 .all(&db.db_con)
2522 .await?;
2523
2524 let crate_metas: Vec<CrateMeta> = cm
2525 .into_iter()
2526 .map(|(m, c)| CrateMeta {
2527 name: c.unwrap().name, id: m.id,
2529 version: m.version,
2530 created: m.created,
2531 downloads: m.downloads,
2532 crate_fk: m.crate_fk,
2533 })
2534 .collect();
2535
2536 Ok(crate_metas)
2537 }
2538}