Skip to main content

kellnr_db/
database.rs

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        // Update the total downloads for the whole crate (all versions)
161        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        // Update the downloads for the specific version
166        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        // Update the total downloads for the whole crate (all versions)
196        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        // Update the downloads for the specific version
201        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            // SAFETY: Unchecked is ok, as only valid crate names are inserted into the database
225            let name = OriginalName::from_unchecked(krate.original_name);
226            // SAFETY: Unchecked is ok, as only valid versions are inserted into the database
227            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        // Delete the entry from the "crate_meta" table
1062        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        // Delete the crate index entry from "crate_index" table
1074        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        // If it was the last entry in the "crate_meta" table, delete the entry
1084        // in the "crate" table as well
1085        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            // Update the max. version if the deleted version was the max. version.
1104            if version == &current_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(); // Safe to unwrap, as crate_meta_rows is not empty
1110                c.max_version = Set(new_max_version.to_string());
1111            }
1112            // Update the ETag value of the crate index.
1113            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()), // Set to empty string, as it can be computed, when the crate index is inserted
1469            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()); // Set to empty string, as it can be computed, when the crate index is inserted
1510            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()), // Set to empty string, as it can be computed, when the crate index is inserted
1524                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        // Exactly one crate must be returned.
1582        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            // Check if the version was yanked or un-yanked and update if so.
1686            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                // Add the meta data for the crate version.
1725                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                    // Entries with invalid events would get skipped
1854                    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
1945// Db methods
1946
1947async 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        // Not a crates.io dependency.
1960        // We cannot know that the crate is from this kellnr instance, but we give it a try.
1961        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    // Delete all existing categories relationships as only the latest list of categories is relevant
2107    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    // Set the latest list of categories for the crate
2113    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        // If the category does not exist, create it
2121        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        // Add the relationship between the crate and the category
2133        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    // Delete all existing keywords relationships as only the latest list of keywords is relevant
2152    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    // Set the latest list of keywords for the crate
2158    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        // If the keyword does not exist, create it
2166        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        // Add the relationship between the crate and the keyword
2178        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    // Delete all existing authors relationships as only the latest list of authors is relevant
2197    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    // Set the latest list of authors for the crate
2203    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        // If the author does not exist, create it
2211        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        // Add the relationship between the crate and the author
2223        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
2368// Test utils
2369
2370pub 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, // Unwarp is ok, as a relation always exists
2547                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}