diff --git a/migration/src/lib.rs b/migration/src/lib.rs index 86c1ae7..6efbd62 100644 --- a/migration/src/lib.rs +++ b/migration/src/lib.rs @@ -2,6 +2,14 @@ #![allow(clippy::wildcard_imports)] pub use sea_orm_migration::prelude::*; mod m20220101_000001_users; +mod m20260517_000001_add_theme_to_users; +mod m20260517_000002_user_roles; +mod m20260517_000003_blog_articles; +mod m20260517_000004_audit_logs; +mod m20260517_000005_audio_albums; +mod m20260517_000006_audio_tracks; +mod m20260517_000007_audio_tags; +mod m20260517_000008_audio_track_tags; pub struct Migrator; @@ -10,6 +18,14 @@ impl MigratorTrait for Migrator { fn migrations() -> Vec> { vec![ Box::new(m20220101_000001_users::Migration), + Box::new(m20260517_000001_add_theme_to_users::Migration), + Box::new(m20260517_000002_user_roles::Migration), + Box::new(m20260517_000003_blog_articles::Migration), + Box::new(m20260517_000004_audit_logs::Migration), + Box::new(m20260517_000005_audio_albums::Migration), + Box::new(m20260517_000006_audio_tracks::Migration), + Box::new(m20260517_000007_audio_tags::Migration), + Box::new(m20260517_000008_audio_track_tags::Migration), // inject-above (do not remove this comment) ] } diff --git a/migration/src/m20260517_000001_add_theme_to_users.rs b/migration/src/m20260517_000001_add_theme_to_users.rs new file mode 100644 index 0000000..e312849 --- /dev/null +++ b/migration/src/m20260517_000001_add_theme_to_users.rs @@ -0,0 +1,24 @@ +use loco_rs::schema::*; +use sea_orm_migration::prelude::*; + +#[derive(DeriveMigrationName)] +pub struct Migration; + +#[async_trait::async_trait] +impl MigrationTrait for Migration { + async fn up(&self, m: &SchemaManager) -> Result<(), DbErr> { + add_column( + m, + "users", + "theme", + ColType::StringLenWithDefault(20, "light".to_string()), + ) + .await?; + Ok(()) + } + + async fn down(&self, m: &SchemaManager) -> Result<(), DbErr> { + remove_column(m, "users", "theme").await?; + Ok(()) + } +} diff --git a/migration/src/m20260517_000002_user_roles.rs b/migration/src/m20260517_000002_user_roles.rs new file mode 100644 index 0000000..11ae66f --- /dev/null +++ b/migration/src/m20260517_000002_user_roles.rs @@ -0,0 +1,97 @@ +use sea_orm_migration::{prelude::*, sea_orm::ConnectionTrait, sea_query::Expr}; + +#[derive(DeriveMigrationName)] +pub struct Migration; + +#[derive(DeriveIden)] +enum UserRoles { + Table, + UserId, + Role, + AssignedBy, + AssignedAt, +} + +#[derive(DeriveIden)] +enum Users { + Table, + Id, +} + +#[async_trait::async_trait] +impl MigrationTrait for Migration { + async fn up(&self, m: &SchemaManager) -> Result<(), DbErr> { + m.create_table( + Table::create() + .table(UserRoles::Table) + .if_not_exists() + .col(ColumnDef::new(UserRoles::UserId).integer().not_null()) + .col(ColumnDef::new(UserRoles::Role).string_len(50).not_null()) + .col(ColumnDef::new(UserRoles::AssignedBy).integer().null()) + .col( + ColumnDef::new(UserRoles::AssignedAt) + .timestamp_with_time_zone() + .not_null() + .default(Expr::current_timestamp()), + ) + .primary_key( + Index::create() + .name("pk-user_roles") + .col(UserRoles::UserId) + .col(UserRoles::Role), + ) + .foreign_key( + ForeignKey::create() + .name("fk-user_roles-user_id-to-users") + .from(UserRoles::Table, UserRoles::UserId) + .to(Users::Table, Users::Id) + .on_delete(ForeignKeyAction::Cascade) + .on_update(ForeignKeyAction::Cascade), + ) + .foreign_key( + ForeignKey::create() + .name("fk-user_roles-assigned_by-to-users") + .from(UserRoles::Table, UserRoles::AssignedBy) + .to(Users::Table, Users::Id) + .on_delete(ForeignKeyAction::SetNull) + .on_update(ForeignKeyAction::NoAction), + ) + .to_owned(), + ) + .await?; + + m.create_index( + Index::create() + .name("idx-user_roles-user_id") + .table(UserRoles::Table) + .col(UserRoles::UserId) + .to_owned(), + ) + .await?; + + let sql = match m.get_database_backend() { + sea_orm_migration::sea_orm::DatabaseBackend::Postgres => { + "INSERT INTO user_roles (user_id, role, assigned_at) \ + SELECT id, 'user', CURRENT_TIMESTAMP FROM users \ + ON CONFLICT (user_id, role) DO NOTHING" + } + sea_orm_migration::sea_orm::DatabaseBackend::Sqlite => { + "INSERT OR IGNORE INTO user_roles (user_id, role, assigned_at) \ + SELECT id, 'user', CURRENT_TIMESTAMP FROM users" + } + sea_orm_migration::sea_orm::DatabaseBackend::MySql => { + "INSERT IGNORE INTO user_roles (user_id, role, assigned_at) \ + SELECT id, 'user', CURRENT_TIMESTAMP FROM users" + } + }; + m.get_connection().execute_unprepared(sql).await?; + + Ok(()) + } + + async fn down(&self, m: &SchemaManager) -> Result<(), DbErr> { + m.drop_table(Table::drop().table(UserRoles::Table).to_owned()) + .await?; + Ok(()) + } +} diff --git a/migration/src/m20260517_000003_blog_articles.rs b/migration/src/m20260517_000003_blog_articles.rs new file mode 100644 index 0000000..3cb95b6 --- /dev/null +++ b/migration/src/m20260517_000003_blog_articles.rs @@ -0,0 +1,126 @@ +use sea_orm_migration::{prelude::*, sea_query::Expr}; + +#[derive(DeriveMigrationName)] +pub struct Migration; + +#[derive(DeriveIden)] +enum BlogArticles { + Table, + Id, + Title, + Slug, + Content, + Excerpt, + Published, + AuthorId, + FeaturedImageId, + ViewCount, + CreatedAt, + UpdatedAt, + PublishedAt, +} + +#[derive(DeriveIden)] +enum Users { + Table, + Id, +} + +#[async_trait::async_trait] +impl MigrationTrait for Migration { + async fn up(&self, m: &SchemaManager) -> Result<(), DbErr> { + m.create_table( + Table::create() + .table(BlogArticles::Table) + .if_not_exists() + .col(ColumnDef::new(BlogArticles::Id).uuid().not_null().primary_key()) + .col(ColumnDef::new(BlogArticles::Title).string_len(500).not_null()) + .col( + ColumnDef::new(BlogArticles::Slug) + .string_len(500) + .not_null() + .unique_key(), + ) + .col(ColumnDef::new(BlogArticles::Content).text().not_null()) + .col(ColumnDef::new(BlogArticles::Excerpt).string_len(1000).null()) + .col( + ColumnDef::new(BlogArticles::Published) + .boolean() + .not_null() + .default(false), + ) + .col(ColumnDef::new(BlogArticles::AuthorId).integer().not_null()) + .col( + ColumnDef::new(BlogArticles::FeaturedImageId) + .string_len(500) + .null(), + ) + .col( + ColumnDef::new(BlogArticles::ViewCount) + .integer() + .not_null() + .default(0), + ) + .col( + ColumnDef::new(BlogArticles::CreatedAt) + .timestamp_with_time_zone() + .not_null() + .default(Expr::current_timestamp()), + ) + .col( + ColumnDef::new(BlogArticles::UpdatedAt) + .timestamp_with_time_zone() + .not_null() + .default(Expr::current_timestamp()), + ) + .col( + ColumnDef::new(BlogArticles::PublishedAt) + .timestamp_with_time_zone() + .null(), + ) + .foreign_key( + ForeignKey::create() + .name("fk-blog_articles-author_id-to-users") + .from(BlogArticles::Table, BlogArticles::AuthorId) + .to(Users::Table, Users::Id) + .on_delete(ForeignKeyAction::Cascade) + .on_update(ForeignKeyAction::Cascade), + ) + .to_owned(), + ) + .await?; + + create_index(m, "idx-blog_articles-slug", BlogArticles::Slug).await?; + m.create_index( + Index::create() + .name("idx-blog_articles-published-published_at") + .table(BlogArticles::Table) + .col(BlogArticles::Published) + .col(BlogArticles::PublishedAt) + .to_owned(), + ) + .await?; + create_index(m, "idx-blog_articles-author_id", BlogArticles::AuthorId).await?; + Ok(()) + } + + async fn down(&self, m: &SchemaManager) -> Result<(), DbErr> { + m.drop_table(Table::drop().table(BlogArticles::Table).to_owned()) + .await?; + Ok(()) + } +} + +async fn create_index(m: &SchemaManager<'_>, name: &str, col: T) -> Result<(), DbErr> +where + T: Iden + 'static, +{ + m.create_index( + Index::create() + .name(name) + .table(BlogArticles::Table) + .col(col) + .to_owned(), + ) + .await +} diff --git a/migration/src/m20260517_000004_audit_logs.rs b/migration/src/m20260517_000004_audit_logs.rs new file mode 100644 index 0000000..5241a7b --- /dev/null +++ b/migration/src/m20260517_000004_audit_logs.rs @@ -0,0 +1,93 @@ +use sea_orm_migration::{prelude::*, sea_query::Expr}; + +#[derive(DeriveMigrationName)] +pub struct Migration; + +#[derive(DeriveIden)] +enum AuditLogs { + Table, + Id, + AdminUserId, + Action, + TargetType, + TargetId, + Details, + IpAddress, + UserAgent, + CreatedAt, +} + +#[derive(DeriveIden)] +enum Users { + Table, + Id, +} + +#[async_trait::async_trait] +impl MigrationTrait for Migration { + async fn up(&self, m: &SchemaManager) -> Result<(), DbErr> { + m.create_table( + Table::create() + .table(AuditLogs::Table) + .if_not_exists() + .col(ColumnDef::new(AuditLogs::Id).uuid().not_null().primary_key()) + .col(ColumnDef::new(AuditLogs::AdminUserId).integer().not_null()) + .col(ColumnDef::new(AuditLogs::Action).string_len(100).not_null()) + .col(ColumnDef::new(AuditLogs::TargetType).string_len(50).null()) + .col(ColumnDef::new(AuditLogs::TargetId).uuid().null()) + .col(ColumnDef::new(AuditLogs::Details).json_binary().null()) + .col(ColumnDef::new(AuditLogs::IpAddress).inet().null()) + .col(ColumnDef::new(AuditLogs::UserAgent).text().null()) + .col( + ColumnDef::new(AuditLogs::CreatedAt) + .timestamp_with_time_zone() + .not_null() + .default(Expr::current_timestamp()), + ) + .foreign_key( + ForeignKey::create() + .name("fk-audit_logs-admin_user_id-to-users") + .from(AuditLogs::Table, AuditLogs::AdminUserId) + .to(Users::Table, Users::Id) + .on_delete(ForeignKeyAction::Cascade) + .on_update(ForeignKeyAction::Cascade), + ) + .to_owned(), + ) + .await?; + + create_index(m, "idx-audit_logs-admin_user_id", AuditLogs::AdminUserId).await?; + create_index(m, "idx-audit_logs-action", AuditLogs::Action).await?; + m.create_index( + Index::create() + .name("idx-audit_logs-target") + .table(AuditLogs::Table) + .col(AuditLogs::TargetType) + .col(AuditLogs::TargetId) + .to_owned(), + ) + .await?; + create_index(m, "idx-audit_logs-created_at", AuditLogs::CreatedAt).await?; + Ok(()) + } + + async fn down(&self, m: &SchemaManager) -> Result<(), DbErr> { + m.drop_table(Table::drop().table(AuditLogs::Table).to_owned()) + .await?; + Ok(()) + } +} + +async fn create_index(m: &SchemaManager<'_>, name: &str, col: T) -> Result<(), DbErr> +where + T: Iden + 'static, +{ + m.create_index( + Index::create() + .name(name) + .table(AuditLogs::Table) + .col(col) + .to_owned(), + ) + .await +} diff --git a/migration/src/m20260517_000005_audio_albums.rs b/migration/src/m20260517_000005_audio_albums.rs new file mode 100644 index 0000000..2564b96 --- /dev/null +++ b/migration/src/m20260517_000005_audio_albums.rs @@ -0,0 +1,120 @@ +use sea_orm_migration::{prelude::*, sea_query::Expr}; + +#[derive(DeriveMigrationName)] +pub struct Migration; + +#[derive(DeriveIden)] +enum AudioAlbums { + Table, + Id, + Title, + Slug, + Description, + CoverImageId, + Artist, + ReleaseDate, + Published, + UploaderId, + ViewCount, + CreatedAt, + UpdatedAt, + PublishedAt, +} + +#[derive(DeriveIden)] +enum Users { + Table, + Id, +} + +#[async_trait::async_trait] +impl MigrationTrait for Migration { + async fn up(&self, m: &SchemaManager) -> Result<(), DbErr> { + m.create_table( + Table::create() + .table(AudioAlbums::Table) + .if_not_exists() + .col(ColumnDef::new(AudioAlbums::Id).uuid().not_null().primary_key()) + .col(ColumnDef::new(AudioAlbums::Title).string_len(500).not_null()) + .col( + ColumnDef::new(AudioAlbums::Slug) + .string_len(500) + .not_null() + .unique_key(), + ) + .col(ColumnDef::new(AudioAlbums::Description).text().null()) + .col( + ColumnDef::new(AudioAlbums::CoverImageId) + .string_len(500) + .null(), + ) + .col(ColumnDef::new(AudioAlbums::Artist).string_len(500).null()) + .col(ColumnDef::new(AudioAlbums::ReleaseDate).date().null()) + .col( + ColumnDef::new(AudioAlbums::Published) + .boolean() + .not_null() + .default(false), + ) + .col(ColumnDef::new(AudioAlbums::UploaderId).integer().not_null()) + .col( + ColumnDef::new(AudioAlbums::ViewCount) + .integer() + .not_null() + .default(0), + ) + .col( + ColumnDef::new(AudioAlbums::CreatedAt) + .timestamp_with_time_zone() + .not_null() + .default(Expr::current_timestamp()), + ) + .col( + ColumnDef::new(AudioAlbums::UpdatedAt) + .timestamp_with_time_zone() + .not_null() + .default(Expr::current_timestamp()), + ) + .col( + ColumnDef::new(AudioAlbums::PublishedAt) + .timestamp_with_time_zone() + .null(), + ) + .foreign_key( + ForeignKey::create() + .name("fk-audio_albums-uploader_id-to-users") + .from(AudioAlbums::Table, AudioAlbums::UploaderId) + .to(Users::Table, Users::Id) + .on_delete(ForeignKeyAction::Cascade) + .on_update(ForeignKeyAction::Cascade), + ) + .to_owned(), + ) + .await?; + + create_index(m, "idx-audio_albums-slug", AudioAlbums::Slug).await?; + create_index(m, "idx-audio_albums-published", AudioAlbums::Published).await?; + create_index(m, "idx-audio_albums-uploader_id", AudioAlbums::UploaderId).await?; + Ok(()) + } + + async fn down(&self, m: &SchemaManager) -> Result<(), DbErr> { + m.drop_table(Table::drop().table(AudioAlbums::Table).to_owned()) + .await?; + Ok(()) + } +} + +async fn create_index(m: &SchemaManager<'_>, name: &str, col: T) -> Result<(), DbErr> +where + T: Iden + 'static, +{ + m.create_index( + Index::create() + .name(name) + .table(AudioAlbums::Table) + .col(col) + .to_owned(), + ) + .await +} diff --git a/migration/src/m20260517_000006_audio_tracks.rs b/migration/src/m20260517_000006_audio_tracks.rs new file mode 100644 index 0000000..e69a6a0 --- /dev/null +++ b/migration/src/m20260517_000006_audio_tracks.rs @@ -0,0 +1,100 @@ +use sea_orm_migration::{prelude::*, sea_query::Expr}; + +#[derive(DeriveMigrationName)] +pub struct Migration; + +#[derive(DeriveIden)] +enum AudioTracks { + Table, + Id, + AlbumId, + Title, + Slug, + AudioFileId, + TrackNumber, + Duration, + Featured, + PlayCount, + CreatedAt, + UpdatedAt, +} + +#[derive(DeriveIden)] +enum AudioAlbums { + Table, + Id, +} + +#[async_trait::async_trait] +impl MigrationTrait for Migration { + async fn up(&self, m: &SchemaManager) -> Result<(), DbErr> { + m.create_table( + Table::create() + .table(AudioTracks::Table) + .if_not_exists() + .col(ColumnDef::new(AudioTracks::Id).uuid().not_null().primary_key()) + .col(ColumnDef::new(AudioTracks::AlbumId).uuid().not_null()) + .col(ColumnDef::new(AudioTracks::Title).string_len(500).not_null()) + .col(ColumnDef::new(AudioTracks::Slug).string_len(500).not_null()) + .col( + ColumnDef::new(AudioTracks::AudioFileId) + .string_len(500) + .not_null(), + ) + .col(ColumnDef::new(AudioTracks::TrackNumber).integer().null()) + .col(ColumnDef::new(AudioTracks::Duration).integer().null()) + .col( + ColumnDef::new(AudioTracks::Featured) + .boolean() + .not_null() + .default(false), + ) + .col( + ColumnDef::new(AudioTracks::PlayCount) + .integer() + .not_null() + .default(0), + ) + .col( + ColumnDef::new(AudioTracks::CreatedAt) + .timestamp_with_time_zone() + .not_null() + .default(Expr::current_timestamp()), + ) + .col( + ColumnDef::new(AudioTracks::UpdatedAt) + .timestamp_with_time_zone() + .not_null() + .default(Expr::current_timestamp()), + ) + .foreign_key( + ForeignKey::create() + .name("fk-audio_tracks-album_id-to-audio_albums") + .from(AudioTracks::Table, AudioTracks::AlbumId) + .to(AudioAlbums::Table, AudioAlbums::Id) + .on_delete(ForeignKeyAction::Cascade) + .on_update(ForeignKeyAction::Cascade), + ) + .to_owned(), + ) + .await?; + + m.create_index( + Index::create() + .name("idx-audio_tracks-album_id-slug") + .table(AudioTracks::Table) + .col(AudioTracks::AlbumId) + .col(AudioTracks::Slug) + .unique() + .to_owned(), + ) + .await?; + Ok(()) + } + + async fn down(&self, m: &SchemaManager) -> Result<(), DbErr> { + m.drop_table(Table::drop().table(AudioTracks::Table).to_owned()) + .await?; + Ok(()) + } +} diff --git a/migration/src/m20260517_000007_audio_tags.rs b/migration/src/m20260517_000007_audio_tags.rs new file mode 100644 index 0000000..b8e62a9 --- /dev/null +++ b/migration/src/m20260517_000007_audio_tags.rs @@ -0,0 +1,52 @@ +use sea_orm_migration::{prelude::*, sea_query::Expr}; + +#[derive(DeriveMigrationName)] +pub struct Migration; + +#[derive(DeriveIden)] +enum AudioTags { + Table, + Id, + Name, + Slug, + CreatedAt, +} + +#[async_trait::async_trait] +impl MigrationTrait for Migration { + async fn up(&self, m: &SchemaManager) -> Result<(), DbErr> { + m.create_table( + Table::create() + .table(AudioTags::Table) + .if_not_exists() + .col(ColumnDef::new(AudioTags::Id).uuid().not_null().primary_key()) + .col( + ColumnDef::new(AudioTags::Name) + .string_len(100) + .not_null() + .unique_key(), + ) + .col( + ColumnDef::new(AudioTags::Slug) + .string_len(100) + .not_null() + .unique_key(), + ) + .col( + ColumnDef::new(AudioTags::CreatedAt) + .timestamp_with_time_zone() + .not_null() + .default(Expr::current_timestamp()), + ) + .to_owned(), + ) + .await?; + Ok(()) + } + + async fn down(&self, m: &SchemaManager) -> Result<(), DbErr> { + m.drop_table(Table::drop().table(AudioTags::Table).to_owned()) + .await?; + Ok(()) + } +} diff --git a/migration/src/m20260517_000008_audio_track_tags.rs b/migration/src/m20260517_000008_audio_track_tags.rs new file mode 100644 index 0000000..cc3ac34 --- /dev/null +++ b/migration/src/m20260517_000008_audio_track_tags.rs @@ -0,0 +1,74 @@ +use sea_orm_migration::{prelude::*, sea_query::Expr}; + +#[derive(DeriveMigrationName)] +pub struct Migration; + +#[derive(DeriveIden)] +enum AudioTrackTags { + Table, + TrackId, + TagId, + CreatedAt, +} + +#[derive(DeriveIden)] +enum AudioTracks { + Table, + Id, +} + +#[derive(DeriveIden)] +enum AudioTags { + Table, + Id, +} + +#[async_trait::async_trait] +impl MigrationTrait for Migration { + async fn up(&self, m: &SchemaManager) -> Result<(), DbErr> { + m.create_table( + Table::create() + .table(AudioTrackTags::Table) + .if_not_exists() + .col(ColumnDef::new(AudioTrackTags::TrackId).uuid().not_null()) + .col(ColumnDef::new(AudioTrackTags::TagId).uuid().not_null()) + .col( + ColumnDef::new(AudioTrackTags::CreatedAt) + .timestamp_with_time_zone() + .not_null() + .default(Expr::current_timestamp()), + ) + .primary_key( + Index::create() + .name("pk-audio_track_tags") + .col(AudioTrackTags::TrackId) + .col(AudioTrackTags::TagId), + ) + .foreign_key( + ForeignKey::create() + .name("fk-audio_track_tags-track_id-to-audio_tracks") + .from(AudioTrackTags::Table, AudioTrackTags::TrackId) + .to(AudioTracks::Table, AudioTracks::Id) + .on_delete(ForeignKeyAction::Cascade) + .on_update(ForeignKeyAction::Cascade), + ) + .foreign_key( + ForeignKey::create() + .name("fk-audio_track_tags-tag_id-to-audio_tags") + .from(AudioTrackTags::Table, AudioTrackTags::TagId) + .to(AudioTags::Table, AudioTags::Id) + .on_delete(ForeignKeyAction::Cascade) + .on_update(ForeignKeyAction::Cascade), + ) + .to_owned(), + ) + .await?; + Ok(()) + } + + async fn down(&self, m: &SchemaManager) -> Result<(), DbErr> { + m.drop_table(Table::drop().table(AudioTrackTags::Table).to_owned()) + .await?; + Ok(()) + } +} diff --git a/src/models/_entities/audio_albums.rs b/src/models/_entities/audio_albums.rs new file mode 100644 index 0000000..c92b088 --- /dev/null +++ b/src/models/_entities/audio_albums.rs @@ -0,0 +1,51 @@ +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20 + +use sea_orm::entity::prelude::*; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)] +#[sea_orm(table_name = "audio_albums")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub id: Uuid, + pub title: String, + #[sea_orm(unique)] + pub slug: String, + #[sea_orm(column_type = "Text", nullable)] + pub description: Option, + pub cover_image_id: Option, + pub artist: Option, + pub release_date: Option, + pub published: bool, + pub uploader_id: i32, + pub view_count: i32, + pub created_at: DateTimeWithTimeZone, + pub updated_at: DateTimeWithTimeZone, + pub published_at: Option, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm(has_many = "super::audio_tracks::Entity")] + AudioTracks, + #[sea_orm( + belongs_to = "super::users::Entity", + from = "Column::UploaderId", + to = "super::users::Column::Id", + on_update = "Cascade", + on_delete = "Cascade" + )] + Users, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::AudioTracks.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Users.def() + } +} diff --git a/src/models/_entities/audio_tags.rs b/src/models/_entities/audio_tags.rs new file mode 100644 index 0000000..2edad2f --- /dev/null +++ b/src/models/_entities/audio_tags.rs @@ -0,0 +1,37 @@ +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20 + +use sea_orm::entity::prelude::*; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)] +#[sea_orm(table_name = "audio_tags")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub id: Uuid, + #[sea_orm(unique)] + pub name: String, + #[sea_orm(unique)] + pub slug: String, + pub created_at: DateTimeWithTimeZone, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm(has_many = "super::audio_track_tags::Entity")] + AudioTrackTags, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::AudioTrackTags.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + super::audio_track_tags::Relation::AudioTracks.def() + } + fn via() -> Option { + Some(super::audio_track_tags::Relation::AudioTags.def().rev()) + } +} diff --git a/src/models/_entities/audio_track_tags.rs b/src/models/_entities/audio_track_tags.rs new file mode 100644 index 0000000..cccfe9f --- /dev/null +++ b/src/models/_entities/audio_track_tags.rs @@ -0,0 +1,46 @@ +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20 + +use sea_orm::entity::prelude::*; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)] +#[sea_orm(table_name = "audio_track_tags")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub track_id: Uuid, + #[sea_orm(primary_key, auto_increment = false)] + pub tag_id: Uuid, + pub created_at: DateTimeWithTimeZone, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::audio_tags::Entity", + from = "Column::TagId", + to = "super::audio_tags::Column::Id", + on_update = "Cascade", + on_delete = "Cascade" + )] + AudioTags, + #[sea_orm( + belongs_to = "super::audio_tracks::Entity", + from = "Column::TrackId", + to = "super::audio_tracks::Column::Id", + on_update = "Cascade", + on_delete = "Cascade" + )] + AudioTracks, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::AudioTags.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::AudioTracks.def() + } +} diff --git a/src/models/_entities/audio_tracks.rs b/src/models/_entities/audio_tracks.rs new file mode 100644 index 0000000..44a79fd --- /dev/null +++ b/src/models/_entities/audio_tracks.rs @@ -0,0 +1,56 @@ +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20 + +use sea_orm::entity::prelude::*; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)] +#[sea_orm(table_name = "audio_tracks")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub id: Uuid, + pub album_id: Uuid, + pub title: String, + pub slug: String, + pub audio_file_id: String, + pub track_number: Option, + pub duration: Option, + pub featured: bool, + pub play_count: i32, + pub created_at: DateTimeWithTimeZone, + pub updated_at: DateTimeWithTimeZone, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::audio_albums::Entity", + from = "Column::AlbumId", + to = "super::audio_albums::Column::Id", + on_update = "Cascade", + on_delete = "Cascade" + )] + AudioAlbums, + #[sea_orm(has_many = "super::audio_track_tags::Entity")] + AudioTrackTags, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::AudioAlbums.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::AudioTrackTags.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + super::audio_track_tags::Relation::AudioTags.def() + } + fn via() -> Option { + Some(super::audio_track_tags::Relation::AudioTracks.def().rev()) + } +} diff --git a/src/models/_entities/audit_logs.rs b/src/models/_entities/audit_logs.rs new file mode 100644 index 0000000..c2d7791 --- /dev/null +++ b/src/models/_entities/audit_logs.rs @@ -0,0 +1,40 @@ +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20 + +use sea_orm::entity::prelude::*; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)] +#[sea_orm(table_name = "audit_logs")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub id: Uuid, + pub admin_user_id: i32, + pub action: String, + pub target_type: Option, + pub target_id: Option, + #[sea_orm(column_type = "JsonBinary", nullable)] + pub details: Option, + #[sea_orm(column_type = "custom(\"inet\")", nullable)] + pub ip_address: Option, + #[sea_orm(column_type = "Text", nullable)] + pub user_agent: Option, + pub created_at: DateTimeWithTimeZone, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::users::Entity", + from = "Column::AdminUserId", + to = "super::users::Column::Id", + on_update = "Cascade", + on_delete = "Cascade" + )] + Users, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Users.def() + } +} diff --git a/src/models/_entities/blog_articles.rs b/src/models/_entities/blog_articles.rs new file mode 100644 index 0000000..91345db --- /dev/null +++ b/src/models/_entities/blog_articles.rs @@ -0,0 +1,42 @@ +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20 + +use sea_orm::entity::prelude::*; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)] +#[sea_orm(table_name = "blog_articles")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub id: Uuid, + pub title: String, + #[sea_orm(unique)] + pub slug: String, + #[sea_orm(column_type = "Text")] + pub content: String, + pub excerpt: Option, + pub published: bool, + pub author_id: i32, + pub featured_image_id: Option, + pub view_count: i32, + pub created_at: DateTimeWithTimeZone, + pub updated_at: DateTimeWithTimeZone, + pub published_at: Option, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::users::Entity", + from = "Column::AuthorId", + to = "super::users::Column::Id", + on_update = "Cascade", + on_delete = "Cascade" + )] + Users, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Users.def() + } +} diff --git a/src/models/_entities/mod.rs b/src/models/_entities/mod.rs index 7efa3a0..2a6ae8d 100644 --- a/src/models/_entities/mod.rs +++ b/src/models/_entities/mod.rs @@ -1,4 +1,12 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20 pub mod prelude; + +pub mod audio_albums; +pub mod audio_tags; +pub mod audio_track_tags; +pub mod audio_tracks; +pub mod audit_logs; +pub mod blog_articles; +pub mod user_roles; pub mod users; diff --git a/src/models/_entities/prelude.rs b/src/models/_entities/prelude.rs index 1055169..61f6b71 100644 --- a/src/models/_entities/prelude.rs +++ b/src/models/_entities/prelude.rs @@ -1,2 +1,10 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20 + +pub use super::audio_albums::Entity as AudioAlbums; +pub use super::audio_tags::Entity as AudioTags; +pub use super::audio_track_tags::Entity as AudioTrackTags; +pub use super::audio_tracks::Entity as AudioTracks; +pub use super::audit_logs::Entity as AuditLogs; +pub use super::blog_articles::Entity as BlogArticles; +pub use super::user_roles::Entity as UserRoles; pub use super::users::Entity as Users; diff --git a/src/models/_entities/user_roles.rs b/src/models/_entities/user_roles.rs new file mode 100644 index 0000000..7d0b607 --- /dev/null +++ b/src/models/_entities/user_roles.rs @@ -0,0 +1,35 @@ +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20 + +use sea_orm::entity::prelude::*; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)] +#[sea_orm(table_name = "user_roles")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub user_id: i32, + #[sea_orm(primary_key, auto_increment = false)] + pub role: String, + pub assigned_by: Option, + pub assigned_at: DateTimeWithTimeZone, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::users::Entity", + from = "Column::AssignedBy", + to = "super::users::Column::Id", + on_update = "NoAction", + on_delete = "SetNull" + )] + Users2, + #[sea_orm( + belongs_to = "super::users::Entity", + from = "Column::UserId", + to = "super::users::Column::Id", + on_update = "Cascade", + on_delete = "Cascade" + )] + Users1, +} diff --git a/src/models/_entities/users.rs b/src/models/_entities/users.rs index 765e992..3ab969a 100644 --- a/src/models/_entities/users.rs +++ b/src/models/_entities/users.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; @@ -24,7 +24,33 @@ pub struct Model { pub email_verified_at: Option, pub magic_link_token: Option, pub magic_link_expiration: Option, + pub theme: String, } #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] -pub enum Relation {} +pub enum Relation { + #[sea_orm(has_many = "super::audio_albums::Entity")] + AudioAlbums, + #[sea_orm(has_many = "super::audit_logs::Entity")] + AuditLogs, + #[sea_orm(has_many = "super::blog_articles::Entity")] + BlogArticles, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::AudioAlbums.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::AuditLogs.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::BlogArticles.def() + } +} diff --git a/src/models/audio_albums.rs b/src/models/audio_albums.rs new file mode 100644 index 0000000..60f5c66 --- /dev/null +++ b/src/models/audio_albums.rs @@ -0,0 +1,28 @@ +use sea_orm::entity::prelude::*; +pub use super::_entities::audio_albums::{ActiveModel, Model, Entity}; +pub type AudioAlbums = Entity; + +#[async_trait::async_trait] +impl ActiveModelBehavior for ActiveModel { + async fn before_save(self, _db: &C, insert: bool) -> std::result::Result + where + C: ConnectionTrait, + { + if !insert && self.updated_at.is_unchanged() { + let mut this = self; + this.updated_at = sea_orm::ActiveValue::Set(chrono::Utc::now().into()); + Ok(this) + } else { + Ok(self) + } + } +} + +// implement your read-oriented logic here +impl Model {} + +// implement your write-oriented logic here +impl ActiveModel {} + +// implement your custom finders, selectors oriented logic here +impl Entity {} diff --git a/src/models/audio_tags.rs b/src/models/audio_tags.rs new file mode 100644 index 0000000..d7d923e --- /dev/null +++ b/src/models/audio_tags.rs @@ -0,0 +1,22 @@ +use sea_orm::entity::prelude::*; +pub use super::_entities::audio_tags::{ActiveModel, Model, Entity}; +pub type AudioTags = Entity; + +#[async_trait::async_trait] +impl ActiveModelBehavior for ActiveModel { + async fn before_save(self, _db: &C, _insert: bool) -> std::result::Result + where + C: ConnectionTrait, + { + Ok(self) + } +} + +// implement your read-oriented logic here +impl Model {} + +// implement your write-oriented logic here +impl ActiveModel {} + +// implement your custom finders, selectors oriented logic here +impl Entity {} diff --git a/src/models/audio_track_tags.rs b/src/models/audio_track_tags.rs new file mode 100644 index 0000000..002e187 --- /dev/null +++ b/src/models/audio_track_tags.rs @@ -0,0 +1,22 @@ +use sea_orm::entity::prelude::*; +pub use super::_entities::audio_track_tags::{ActiveModel, Model, Entity}; +pub type AudioTrackTags = Entity; + +#[async_trait::async_trait] +impl ActiveModelBehavior for ActiveModel { + async fn before_save(self, _db: &C, _insert: bool) -> std::result::Result + where + C: ConnectionTrait, + { + Ok(self) + } +} + +// implement your read-oriented logic here +impl Model {} + +// implement your write-oriented logic here +impl ActiveModel {} + +// implement your custom finders, selectors oriented logic here +impl Entity {} diff --git a/src/models/audio_tracks.rs b/src/models/audio_tracks.rs new file mode 100644 index 0000000..0edd09e --- /dev/null +++ b/src/models/audio_tracks.rs @@ -0,0 +1,28 @@ +use sea_orm::entity::prelude::*; +pub use super::_entities::audio_tracks::{ActiveModel, Model, Entity}; +pub type AudioTracks = Entity; + +#[async_trait::async_trait] +impl ActiveModelBehavior for ActiveModel { + async fn before_save(self, _db: &C, insert: bool) -> std::result::Result + where + C: ConnectionTrait, + { + if !insert && self.updated_at.is_unchanged() { + let mut this = self; + this.updated_at = sea_orm::ActiveValue::Set(chrono::Utc::now().into()); + Ok(this) + } else { + Ok(self) + } + } +} + +// implement your read-oriented logic here +impl Model {} + +// implement your write-oriented logic here +impl ActiveModel {} + +// implement your custom finders, selectors oriented logic here +impl Entity {} diff --git a/src/models/audit_logs.rs b/src/models/audit_logs.rs new file mode 100644 index 0000000..2db0fae --- /dev/null +++ b/src/models/audit_logs.rs @@ -0,0 +1,22 @@ +use sea_orm::entity::prelude::*; +pub use super::_entities::audit_logs::{ActiveModel, Model, Entity}; +pub type AuditLogs = Entity; + +#[async_trait::async_trait] +impl ActiveModelBehavior for ActiveModel { + async fn before_save(self, _db: &C, _insert: bool) -> std::result::Result + where + C: ConnectionTrait, + { + Ok(self) + } +} + +// implement your read-oriented logic here +impl Model {} + +// implement your write-oriented logic here +impl ActiveModel {} + +// implement your custom finders, selectors oriented logic here +impl Entity {} diff --git a/src/models/blog_articles.rs b/src/models/blog_articles.rs new file mode 100644 index 0000000..e523f5b --- /dev/null +++ b/src/models/blog_articles.rs @@ -0,0 +1,28 @@ +use sea_orm::entity::prelude::*; +pub use super::_entities::blog_articles::{ActiveModel, Model, Entity}; +pub type BlogArticles = Entity; + +#[async_trait::async_trait] +impl ActiveModelBehavior for ActiveModel { + async fn before_save(self, _db: &C, insert: bool) -> std::result::Result + where + C: ConnectionTrait, + { + if !insert && self.updated_at.is_unchanged() { + let mut this = self; + this.updated_at = sea_orm::ActiveValue::Set(chrono::Utc::now().into()); + Ok(this) + } else { + Ok(self) + } + } +} + +// implement your read-oriented logic here +impl Model {} + +// implement your write-oriented logic here +impl ActiveModel {} + +// implement your custom finders, selectors oriented logic here +impl Entity {} diff --git a/src/models/mod.rs b/src/models/mod.rs index 48da463..445e29d 100644 --- a/src/models/mod.rs +++ b/src/models/mod.rs @@ -1,2 +1,9 @@ pub mod _entities; pub mod users; +pub mod user_roles; +pub mod audio_tags; +pub mod audio_tracks; +pub mod audio_track_tags; +pub mod audit_logs; +pub mod blog_articles; +pub mod audio_albums; diff --git a/src/models/user_roles.rs b/src/models/user_roles.rs new file mode 100644 index 0000000..5ea7002 --- /dev/null +++ b/src/models/user_roles.rs @@ -0,0 +1,22 @@ +use sea_orm::entity::prelude::*; +pub use super::_entities::user_roles::{ActiveModel, Model, Entity}; +pub type UserRoles = Entity; + +#[async_trait::async_trait] +impl ActiveModelBehavior for ActiveModel { + async fn before_save(self, _db: &C, _insert: bool) -> std::result::Result + where + C: ConnectionTrait, + { + Ok(self) + } +} + +// implement your read-oriented logic here +impl Model {} + +// implement your write-oriented logic here +impl ActiveModel {} + +// implement your custom finders, selectors oriented logic here +impl Entity {}