From dc33fa8416ce6b447494c6efdf46518da37ac1cc Mon Sep 17 00:00:00 2001 From: Tolmachev Igor Date: Tue, 26 Aug 2025 21:13:53 +0900 Subject: Add database migration and entities --- .zed/tasks.json | 32 +++++++ Cargo.toml | 7 ++ entity/Cargo.toml | 10 ++ entity/src/access_to_queue.rs | 46 +++++++++ entity/src/lib.rs | 9 ++ entity/src/prelude.rs | 6 ++ entity/src/queue_elements.rs | 50 ++++++++++ entity/src/queues.rs | 51 ++++++++++ entity/src/sea_orm_active_enums.rs | 16 ++++ entity/src/users.rs | 49 ++++++++++ migration/Cargo.toml | 13 +++ migration/src/lib.rs | 17 ++++ migration/src/m0_init_tables.rs | 192 +++++++++++++++++++++++++++++++++++++ migration/src/main.rs | 6 ++ 14 files changed, 504 insertions(+) create mode 100644 entity/Cargo.toml create mode 100644 entity/src/access_to_queue.rs create mode 100644 entity/src/lib.rs create mode 100644 entity/src/prelude.rs create mode 100644 entity/src/queue_elements.rs create mode 100644 entity/src/queues.rs create mode 100644 entity/src/sea_orm_active_enums.rs create mode 100644 entity/src/users.rs create mode 100644 migration/Cargo.toml create mode 100644 migration/src/lib.rs create mode 100644 migration/src/m0_init_tables.rs create mode 100644 migration/src/main.rs diff --git a/.zed/tasks.json b/.zed/tasks.json index 4648b2c..e725a43 100644 --- a/.zed/tasks.json +++ b/.zed/tasks.json @@ -1,4 +1,36 @@ [ + { + "label": "Run migrations", + "command": "sea-orm-cli", + "args": ["migrate", "up"], + + "env": { + "DATABASE_URL": "postgres://itmo_queue:itmo_queue@localhost/itmo_queue" + }, + + "use_new_terminal": false, + "allow_concurrent_runs": false, + "reveal": "no_focus", + "reveal_target": "dock", + "hide": "never", + "shell": "system" + }, + { + "label": "Generate entity files", + "command": "sea-orm-cli", + "args": ["generate", "entity", "--lib", "--ignore-tables", "migrations", "-o", "entity/src/"], + + "env": { + "DATABASE_URL": "postgres://itmo_queue:itmo_queue@localhost/itmo_queue" + }, + + "use_new_terminal": false, + "allow_concurrent_runs": false, + "reveal": "no_focus", + "reveal_target": "dock", + "hide": "never", + "shell": "system" + }, { "label": "Run release server", "command": "cargo", diff --git a/Cargo.toml b/Cargo.toml index bd321fd..9b59ed0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,9 +2,16 @@ name = "itmo_queue_server" version = "0.1.0" edition = "2024" +publish = false + +[workspace] +members = [".", "entity", "migration"] [dependencies] axum = "0.8.4" +entity = { version = "0.1.0", path = "entity" } +migration = { version = "0.1.0", path = "migration" } +sea-orm = { version = "1.1.14", features = ["sqlx-postgres", "runtime-tokio-rustls"] } serde = { version = "1.0.219", features = ["derive"] } serde_json = "1.0.143" tokio = { version = "1.47.1", features = ["macros", "rt-multi-thread"] } diff --git a/entity/Cargo.toml b/entity/Cargo.toml new file mode 100644 index 0000000..d212311 --- /dev/null +++ b/entity/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "entity" +version = "0.1.0" +edition = "2024" +publish = false + + +[dependencies] +sea-orm = "1.1.14" +serde = { version = "1.0.219", features = ["derive"] } diff --git a/entity/src/access_to_queue.rs b/entity/src/access_to_queue.rs new file mode 100644 index 0000000..9de03d5 --- /dev/null +++ b/entity/src/access_to_queue.rs @@ -0,0 +1,46 @@ +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.14 + +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] +#[sea_orm(table_name = "access_to_queue")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub user_id: i64, + #[sea_orm(primary_key, auto_increment = false)] + pub queue_id: i64, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::queues::Entity", + from = "Column::QueueId", + to = "super::queues::Column::Id", + on_update = "Cascade", + on_delete = "Cascade" + )] + Queues, + #[sea_orm( + belongs_to = "super::users::Entity", + from = "Column::UserId", + to = "super::users::Column::Id", + on_update = "Cascade", + on_delete = "Cascade" + )] + Users, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Queues.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Users.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/entity/src/lib.rs b/entity/src/lib.rs new file mode 100644 index 0000000..1ca05d6 --- /dev/null +++ b/entity/src/lib.rs @@ -0,0 +1,9 @@ +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.14 + +pub mod prelude; + +pub mod access_to_queue; +pub mod queue_elements; +pub mod queues; +pub mod sea_orm_active_enums; +pub mod users; diff --git a/entity/src/prelude.rs b/entity/src/prelude.rs new file mode 100644 index 0000000..60f72f9 --- /dev/null +++ b/entity/src/prelude.rs @@ -0,0 +1,6 @@ +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.14 + +pub use super::access_to_queue::Entity as AccessToQueue; +pub use super::queue_elements::Entity as QueueElements; +pub use super::queues::Entity as Queues; +pub use super::users::Entity as Users; diff --git a/entity/src/queue_elements.rs b/entity/src/queue_elements.rs new file mode 100644 index 0000000..7002da1 --- /dev/null +++ b/entity/src/queue_elements.rs @@ -0,0 +1,50 @@ +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.14 + +use super::sea_orm_active_enums::QueueElementStatusEnum; +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] +#[sea_orm(table_name = "queue_elements")] +pub struct Model { + #[sea_orm(primary_key)] + pub id: i64, + pub queue_id: i64, + pub user_id: i64, + #[sea_orm(unique)] + pub position: i64, + pub status: QueueElementStatusEnum, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::queues::Entity", + from = "Column::QueueId", + to = "super::queues::Column::Id", + on_update = "Cascade", + on_delete = "Cascade" + )] + Queues, + #[sea_orm( + belongs_to = "super::users::Entity", + from = "Column::UserId", + to = "super::users::Column::Id", + on_update = "Cascade", + on_delete = "Cascade" + )] + Users, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Queues.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Users.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/entity/src/queues.rs b/entity/src/queues.rs new file mode 100644 index 0000000..34b358a --- /dev/null +++ b/entity/src/queues.rs @@ -0,0 +1,51 @@ +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.14 + +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] +#[sea_orm(table_name = "queues")] +pub struct Model { + #[sea_orm(primary_key)] + pub id: i64, + pub owner_id: i64, + pub name: String, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm(has_many = "super::access_to_queue::Entity")] + AccessToQueue, + #[sea_orm(has_many = "super::queue_elements::Entity")] + QueueElements, + #[sea_orm( + belongs_to = "super::users::Entity", + from = "Column::OwnerId", + to = "super::users::Column::Id", + on_update = "Cascade", + on_delete = "Cascade" + )] + Users, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::AccessToQueue.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::QueueElements.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + super::access_to_queue::Relation::Users.def() + } + fn via() -> Option { + Some(super::access_to_queue::Relation::Queues.def().rev()) + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/entity/src/sea_orm_active_enums.rs b/entity/src/sea_orm_active_enums.rs new file mode 100644 index 0000000..2252a9f --- /dev/null +++ b/entity/src/sea_orm_active_enums.rs @@ -0,0 +1,16 @@ +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.14 + +use sea_orm::entity::prelude::*; + +#[derive(Debug, Clone, PartialEq, Eq, EnumIter, DeriveActiveEnum)] +#[sea_orm( + rs_type = "String", + db_type = "Enum", + enum_name = "queue_element_status_enum" +)] +pub enum QueueElementStatusEnum { + #[sea_orm(string_value = "passed")] + Passed, + #[sea_orm(string_value = "waiting")] + Waiting, +} diff --git a/entity/src/users.rs b/entity/src/users.rs new file mode 100644 index 0000000..b61d51b --- /dev/null +++ b/entity/src/users.rs @@ -0,0 +1,49 @@ +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.14 + +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] +#[sea_orm(table_name = "users")] +pub struct Model { + #[sea_orm(primary_key)] + pub id: i64, + #[sea_orm(unique)] + pub login: String, + pub password: String, + pub password_issue_date: DateTime, + pub first_name: String, + pub last_name: String, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm(has_many = "super::access_to_queue::Entity")] + AccessToQueue, + #[sea_orm(has_many = "super::queue_elements::Entity")] + QueueElements, + #[sea_orm(has_many = "super::queues::Entity")] + Queues, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::AccessToQueue.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::QueueElements.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + super::access_to_queue::Relation::Queues.def() + } + fn via() -> Option { + Some(super::access_to_queue::Relation::Users.def().rev()) + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/migration/Cargo.toml b/migration/Cargo.toml new file mode 100644 index 0000000..bb33242 --- /dev/null +++ b/migration/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "migration" +version = "0.1.0" +edition = "2024" +publish = false + +[lib] +name = "migration" +path = "src/lib.rs" + +[dependencies] +sea-orm-migration = { version = "1.1.14", features = ["sqlx-postgres", "runtime-tokio-rustls"] } +tokio = { version = "1.47.1", features = ["macros", "rt"] } diff --git a/migration/src/lib.rs b/migration/src/lib.rs new file mode 100644 index 0000000..ef740fc --- /dev/null +++ b/migration/src/lib.rs @@ -0,0 +1,17 @@ +#![deny(dead_code)] +mod m0_init_tables; + +use sea_orm_migration::prelude::*; + +pub struct Migrator; + +#[async_trait::async_trait] +impl MigratorTrait for Migrator { + fn migration_table_name() -> DynIden { + Alias::new("migrations").into_iden() + } + + fn migrations() -> Vec> { + vec![Box::new(m0_init_tables::Migration)] + } +} diff --git a/migration/src/m0_init_tables.rs b/migration/src/m0_init_tables.rs new file mode 100644 index 0000000..576f45b --- /dev/null +++ b/migration/src/m0_init_tables.rs @@ -0,0 +1,192 @@ +use sea_orm_migration::prelude::extension::postgres::Type; +use sea_orm_migration::sea_orm::{EnumIter, Iterable}; +use sea_orm_migration::{prelude::*, schema::*}; + +#[derive(DeriveIden)] +enum Users { + Table, + Id, + Login, + Password, + PasswordIssueDate, + FirstName, + LastName, +} + +#[derive(DeriveIden)] +enum Queues { + Table, + Id, + OwnerId, + Name, +} + +#[derive(DeriveIden)] +enum AccessToQueue { + Table, + UserId, + QueueId, +} + +#[derive(DeriveIden)] +enum QueueElements { + Table, + Id, + QueueId, + UserId, + Position, + Status, +} + +mod queue_element_status { + use super::*; + + #[derive(DeriveIden)] + #[sea_orm(iden = "queue_element_status_enum")] + pub struct Iden; + + #[derive(Iden, EnumIter)] + pub enum Items { + Passed, + Waiting, + } +} + +#[derive(DeriveMigrationName)] +pub struct Migration; + +#[async_trait::async_trait] +impl MigrationTrait for Migration { + async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .create_table( + Table::create() + .table(Users::Table) + .if_not_exists() + .col(pk_auto(Users::Id).big_integer()) + .col(string(Users::Login).unique_key()) + .col(string(Users::Password)) + .col(timestamp(Users::PasswordIssueDate)) + .col(string(Users::FirstName)) + .col(string(Users::LastName)) + .to_owned(), + ) + .await?; + + manager + .create_table( + Table::create() + .table(Queues::Table) + .if_not_exists() + .col(pk_auto(Queues::Id).big_integer()) + .col(big_integer(Queues::OwnerId)) + .foreign_key( + ForeignKey::create() + .from(Queues::Table, Queues::OwnerId) + .to(Users::Table, Users::Id) + .on_delete(ForeignKeyAction::Cascade) + .on_update(ForeignKeyAction::Cascade), + ) + .col(string(Queues::Name)) + .to_owned(), + ) + .await?; + + manager + .create_table( + Table::create() + .table(AccessToQueue::Table) + .if_not_exists() + .col(big_integer(AccessToQueue::UserId)) + .col(big_integer(AccessToQueue::QueueId)) + .foreign_key( + ForeignKey::create() + .from(AccessToQueue::Table, AccessToQueue::UserId) + .to(Users::Table, Users::Id) + .on_delete(ForeignKeyAction::Cascade) + .on_update(ForeignKeyAction::Cascade), + ) + .foreign_key( + ForeignKey::create() + .from(AccessToQueue::Table, AccessToQueue::QueueId) + .to(Queues::Table, Queues::Id) + .on_delete(ForeignKeyAction::Cascade) + .on_update(ForeignKeyAction::Cascade), + ) + .primary_key( + Index::create() + .col(AccessToQueue::UserId) + .col(AccessToQueue::QueueId), + ) + .to_owned(), + ) + .await?; + + manager + .create_type( + Type::create() + .as_enum(queue_element_status::Iden) + .values(queue_element_status::Items::iter()) + .to_owned(), + ) + .await?; + + manager + .create_table( + Table::create() + .table(QueueElements::Table) + .if_not_exists() + .col(pk_auto(QueueElements::Id).big_integer()) + .col(big_integer(QueueElements::QueueId)) + .col(big_integer(QueueElements::UserId)) + .col( + big_integer(QueueElements::Position) + .unique_key() + .auto_increment(), + ) + .col(enumeration( + QueueElements::Status, + queue_element_status::Iden, + queue_element_status::Items::iter(), + )) + .foreign_key( + ForeignKey::create() + .from(QueueElements::Table, QueueElements::UserId) + .to(Users::Table, Users::Id) + .on_delete(ForeignKeyAction::Cascade) + .on_update(ForeignKeyAction::Cascade), + ) + .foreign_key( + ForeignKey::create() + .from(QueueElements::Table, QueueElements::QueueId) + .to(Queues::Table, Queues::Id) + .on_delete(ForeignKeyAction::Cascade) + .on_update(ForeignKeyAction::Cascade), + ) + .to_owned(), + ) + .await?; + + Ok(()) + } + + async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .drop_table(Table::drop().table(Users::Table).to_owned()) + .await?; + + manager + .drop_table(Table::drop().table(Queues::Table).to_owned()) + .await?; + + manager + .drop_table(Table::drop().table(AccessToQueue::Table).to_owned()) + .await?; + + manager + .drop_table(Table::drop().table(QueueElements::Table).to_owned()) + .await?; + + Ok(()) + } +} diff --git a/migration/src/main.rs b/migration/src/main.rs new file mode 100644 index 0000000..56190ac --- /dev/null +++ b/migration/src/main.rs @@ -0,0 +1,6 @@ +use sea_orm_migration::prelude::*; + +#[tokio::main(flavor = "current_thread")] +async fn main() { + cli::run_cli(migration::Migrator).await; +} -- cgit v1.3