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