From e758452509a0508ace0a5502982ba802c2b59260 Mon Sep 17 00:00:00 2001 From: GW_MC <72297530+GWMCwing@users.noreply.github.com> Date: Sun, 7 Dec 2025 19:08:22 +0800 Subject: [PATCH] Include user table, identity and session table --- public/database/src/generated/entities/mod.rs | 2 + .../src/generated/entities/prelude.rs | 2 + .../src/generated/entities/session.rs | 29 +++++ .../database/src/generated/entities/user.rs | 9 +- .../src/generated/entities/user_identity.rs | 35 ++++++ public/migration/src/lib.rs | 6 +- public/migration/src/migrations.rs | 6 +- ...> m20251011_000001_create_config_table.rs} | 0 ... => m20251011_000002_create_user_table.rs} | 19 +++- ...51011_000003_create_user_identity_table.rs | 102 ++++++++++++++++++ .../m20251011_000004_create_session_table.rs | 86 +++++++++++++++ 11 files changed, 285 insertions(+), 11 deletions(-) create mode 100644 public/database/src/generated/entities/session.rs create mode 100644 public/database/src/generated/entities/user_identity.rs rename public/migration/src/migrations/{m20251011_000002_create_config_table.rs => m20251011_000001_create_config_table.rs} (100%) rename public/migration/src/migrations/{m20251011_000001_create_user_table.rs => m20251011_000002_create_user_table.rs} (77%) create mode 100644 public/migration/src/migrations/m20251011_000003_create_user_identity_table.rs create mode 100644 public/migration/src/migrations/m20251011_000004_create_session_table.rs diff --git a/public/database/src/generated/entities/mod.rs b/public/database/src/generated/entities/mod.rs index f2dcfc6..72511ee 100644 --- a/public/database/src/generated/entities/mod.rs +++ b/public/database/src/generated/entities/mod.rs @@ -3,4 +3,6 @@ pub mod prelude; pub mod config; +pub mod session; pub mod user; +pub mod user_identity; diff --git a/public/database/src/generated/entities/prelude.rs b/public/database/src/generated/entities/prelude.rs index 222250e..e73b8a2 100644 --- a/public/database/src/generated/entities/prelude.rs +++ b/public/database/src/generated/entities/prelude.rs @@ -1,4 +1,6 @@ //! `SeaORM` Entity, @generated by sea-orm-codegen 2.0.0-rc.18 pub use super::config::Entity as Config; +pub use super::session::Entity as Session; pub use super::user::Entity as User; +pub use super::user_identity::Entity as UserIdentity; diff --git a/public/database/src/generated/entities/session.rs b/public/database/src/generated/entities/session.rs new file mode 100644 index 0000000..fc26f9f --- /dev/null +++ b/public/database/src/generated/entities/session.rs @@ -0,0 +1,29 @@ +//! `SeaORM` Entity, @generated by sea-orm-codegen 2.0.0-rc.18 + +use sea_orm::entity::prelude::*; +use serde::{Deserialize, Serialize}; + +#[sea_orm::model] +#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel, Serialize, Deserialize)] +#[sea_orm(table_name = "session")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub id: Uuid, + pub user_id: Uuid, + #[sea_orm(unique)] + pub refresh_token_hash: Option, + pub expires_at: DateTimeUtc, + pub revoked_at: Option, + pub created_at: DateTimeUtc, + pub updated_at: DateTimeUtc, + #[sea_orm( + belongs_to, + from = "user_id", + to = "id", + on_update = "Cascade", + on_delete = "Cascade" + )] + pub user: HasOne, +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/public/database/src/generated/entities/user.rs b/public/database/src/generated/entities/user.rs index 1a415b4..4c01244 100644 --- a/public/database/src/generated/entities/user.rs +++ b/public/database/src/generated/entities/user.rs @@ -12,10 +12,15 @@ pub struct Model { #[sea_orm(unique)] pub name: String, pub is_admin: bool, - pub password_hash: String, - pub salt: String, + pub is_active: bool, pub created_at: DateTimeUtc, pub updated_at: DateTimeUtc, + pub last_login_at: Option, + pub deleted_at: Option, + #[sea_orm(has_many)] + pub sessions: HasMany, + #[sea_orm(has_many)] + pub user_identities: HasMany, } impl ActiveModelBehavior for ActiveModel {} diff --git a/public/database/src/generated/entities/user_identity.rs b/public/database/src/generated/entities/user_identity.rs new file mode 100644 index 0000000..240558d --- /dev/null +++ b/public/database/src/generated/entities/user_identity.rs @@ -0,0 +1,35 @@ +//! `SeaORM` Entity, @generated by sea-orm-codegen 2.0.0-rc.18 + +use sea_orm::entity::prelude::*; +use serde::{Deserialize, Serialize}; + +#[sea_orm::model] +#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel, Serialize, Deserialize)] +#[sea_orm(table_name = "user_identity")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub id: Uuid, + #[sea_orm(unique_key = "provider")] + pub user_id: Uuid, + #[sea_orm(unique_key = "provider")] + pub provider: String, + pub email: Option, + pub password_hash: Option, + pub is_revoked: bool, + #[sea_orm(column_type = "JsonBinary", nullable)] + pub metadata: Option, + pub password_changed_at: Option, + pub revoked_at: Option, + pub created_at: DateTimeUtc, + pub updated_at: DateTimeUtc, + #[sea_orm( + belongs_to, + from = "user_id", + to = "id", + on_update = "Cascade", + on_delete = "Cascade" + )] + pub user: HasOne, +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/public/migration/src/lib.rs b/public/migration/src/lib.rs index 1be42ae..eecb57c 100644 --- a/public/migration/src/lib.rs +++ b/public/migration/src/lib.rs @@ -10,8 +10,10 @@ pub struct Migrator; impl MigratorTrait for Migrator { fn migrations() -> Vec> { vec![ - Box::new(m20251011_000001_create_user_table::Migration), - Box::new(m20251011_000002_create_config_table::Migration), + Box::new(m20251011_000001_create_config_table::Migration), + Box::new(m20251011_000002_create_user_table::Migration), + Box::new(m20251011_000003_create_user_identity_table::Migration), + Box::new(m20251011_000004_create_session_table::Migration), ] } } diff --git a/public/migration/src/migrations.rs b/public/migration/src/migrations.rs index 8597472..5ff5d97 100644 --- a/public/migration/src/migrations.rs +++ b/public/migration/src/migrations.rs @@ -1,2 +1,4 @@ -pub mod m20251011_000001_create_user_table; -pub mod m20251011_000002_create_config_table; +pub mod m20251011_000001_create_config_table; +pub mod m20251011_000002_create_user_table; +pub mod m20251011_000003_create_user_identity_table; +pub mod m20251011_000004_create_session_table; diff --git a/public/migration/src/migrations/m20251011_000002_create_config_table.rs b/public/migration/src/migrations/m20251011_000001_create_config_table.rs similarity index 100% rename from public/migration/src/migrations/m20251011_000002_create_config_table.rs rename to public/migration/src/migrations/m20251011_000001_create_config_table.rs diff --git a/public/migration/src/migrations/m20251011_000001_create_user_table.rs b/public/migration/src/migrations/m20251011_000002_create_user_table.rs similarity index 77% rename from public/migration/src/migrations/m20251011_000001_create_user_table.rs rename to public/migration/src/migrations/m20251011_000002_create_user_table.rs index c568e7f..515ed84 100644 --- a/public/migration/src/migrations/m20251011_000001_create_user_table.rs +++ b/public/migration/src/migrations/m20251011_000002_create_user_table.rs @@ -3,16 +3,19 @@ use sea_orm_migration::{prelude::*, schema::*}; #[derive(DeriveMigrationName)] pub struct Migration; +#[forbid(dead_code)] #[derive(DeriveIden)] -enum User { +pub enum User { Table, Id, // Name, IsAdmin, - PasswordHash, - Salt, + IsActive, // + LastLoginAt, + // + DeletedAt, CreatedAt, UpdatedAt, } @@ -33,8 +36,12 @@ impl MigrationTrait for Migration { .default(false) .not_null(), ) - .col(ColumnDef::new(User::PasswordHash).string().not_null()) - .col(ColumnDef::new(User::Salt).string().not_null()) + .col( + ColumnDef::new(User::IsActive) + .boolean() + .default(true) + .not_null(), + ) .col( ColumnDef::new(User::CreatedAt) .timestamp() @@ -47,6 +54,8 @@ impl MigrationTrait for Migration { .default(SimpleExpr::Keyword(Keyword::CurrentTimestamp)) .not_null(), ) + .col(ColumnDef::new(User::LastLoginAt).timestamp().null()) + .col(ColumnDef::new(User::DeletedAt).timestamp().null()) .to_owned(), ) .await diff --git a/public/migration/src/migrations/m20251011_000003_create_user_identity_table.rs b/public/migration/src/migrations/m20251011_000003_create_user_identity_table.rs new file mode 100644 index 0000000..f85f3e6 --- /dev/null +++ b/public/migration/src/migrations/m20251011_000003_create_user_identity_table.rs @@ -0,0 +1,102 @@ +use sea_orm_migration::{prelude::*, schema::*}; + +#[derive(DeriveMigrationName)] +pub struct Migration; + +#[forbid(dead_code)] +#[derive(DeriveIden)] +pub enum UserIdentity { + Table, + Id, + UserId, + Provider, // e.g. "password". Extensible for plugins like OAuth in the future + // + Email, // optional + PasswordHash, // optional for non-password providers + IsRevoked, // default false + // + Metadata, // for custom provider metadata + // + PasswordChangedAt, + RevokedAt, + // + CreatedAt, + UpdatedAt, +} + +#[async_trait::async_trait] +impl MigrationTrait for Migration { + async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { + let _ = manager + .create_table( + Table::create() + .table(UserIdentity::Table) + .if_not_exists() + .col(pk_uuid(UserIdentity::Id)) + // + .col(ColumnDef::new(UserIdentity::UserId).uuid().not_null()) + .foreign_key( + ForeignKey::create() + .name("fk-user-identity-user-id") + .from(UserIdentity::Table, UserIdentity::UserId) + .to( + super::m20251011_000002_create_user_table::User::Table, + super::m20251011_000002_create_user_table::User::Id, + ) + .on_delete(ForeignKeyAction::Cascade) + .on_update(ForeignKeyAction::Cascade), + ) + .col(ColumnDef::new(UserIdentity::Provider).string().not_null()) + // + .col(ColumnDef::new(UserIdentity::Email).string().null()) + .col(ColumnDef::new(UserIdentity::PasswordHash).string().null()) + .col( + ColumnDef::new(UserIdentity::IsRevoked) + .boolean() + .default(false) + .not_null(), + ) + .col(ColumnDef::new(UserIdentity::Metadata).json_binary().null()) + // + .col( + ColumnDef::new(UserIdentity::PasswordChangedAt) + .timestamp() + .null(), + ) + .col(ColumnDef::new(UserIdentity::RevokedAt).timestamp().null()) + // + .col( + ColumnDef::new(UserIdentity::CreatedAt) + .timestamp() + .default(SimpleExpr::Keyword(Keyword::CurrentTimestamp)) + .not_null(), + ) + .col( + ColumnDef::new(UserIdentity::UpdatedAt) + .timestamp() + .default(SimpleExpr::Keyword(Keyword::CurrentTimestamp)) + .not_null(), + ) + .to_owned(), + ) + .await; + + manager + .create_index( + Index::create() + .name("idx-user-identity-user-id-provider") + .table(UserIdentity::Table) + .col(UserIdentity::UserId) + .col(UserIdentity::Provider) + .unique() + .to_owned(), + ) + .await + } + + async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .drop_table(Table::drop().table(UserIdentity::Table).to_owned()) + .await + } +} diff --git a/public/migration/src/migrations/m20251011_000004_create_session_table.rs b/public/migration/src/migrations/m20251011_000004_create_session_table.rs new file mode 100644 index 0000000..c7ab7f1 --- /dev/null +++ b/public/migration/src/migrations/m20251011_000004_create_session_table.rs @@ -0,0 +1,86 @@ +use sea_orm_migration::{prelude::*, schema::*}; + +#[derive(DeriveMigrationName)] +pub struct Migration; + +#[forbid(dead_code)] +#[derive(DeriveIden)] +pub enum Session { + Table, + Id, + UserId, + // + RefreshTokenHash, + // + ExpiresAt, + RevokedAt, + // + CreatedAt, + UpdatedAt, +} + +#[async_trait::async_trait] +impl MigrationTrait for Migration { + async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { + let _ = manager + .create_table( + Table::create() + .table(Session::Table) + .if_not_exists() + .col(pk_uuid(Session::Id)) + // + .col(ColumnDef::new(Session::UserId).uuid().not_null()) + .foreign_key( + ForeignKey::create() + .name("fk-session-user-id") + .from(Session::Table, Session::UserId) + .to( + super::m20251011_000002_create_user_table::User::Table, + super::m20251011_000002_create_user_table::User::Id, + ) + .on_delete(ForeignKeyAction::Cascade) + .on_update(ForeignKeyAction::Cascade), + ) + .col( + ColumnDef::new(Session::RefreshTokenHash) + .string() + .null() + .unique_key(), + ) + .col(ColumnDef::new(Session::ExpiresAt).timestamp().not_null()) + .col(ColumnDef::new(Session::RevokedAt).timestamp().null()) + // + .col( + ColumnDef::new(Session::CreatedAt) + .timestamp() + .default(SimpleExpr::Keyword(Keyword::CurrentTimestamp)) + .not_null(), + ) + .col( + ColumnDef::new(Session::UpdatedAt) + .timestamp() + .default(SimpleExpr::Keyword(Keyword::CurrentTimestamp)) + .not_null(), + ) + .to_owned(), + ) + .await; + + manager + .create_index( + Index::create() + .name("idx-session-user-id-token") + .table(Session::Table) + .col(Session::UserId) + .col(Session::RefreshTokenHash) + .to_owned(), + ) + .await + } + + async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .drop_table(Table::drop().table(Session::Table).to_owned()) + .await + } +}