team_member.rs 67 lignes · 2260 octets
use chrono::{DateTime, Utc};
use sea_orm::entity::prelude::*;
use serde::{Deserialize, Serialize};
use uuid::Uuid;

/// Membership edge between a (local user OR remote identity) and a team.
///
/// RFC 0018: the composite PK `(team_id, user_id)` was relaxed to a
/// surrogate `id` PK so that remote-only rows (where `user_id` is
/// `NULL`) are addressable. Uniqueness for local rows is preserved by
/// the partial unique index `team_members_local_unique`. Mutual
/// exclusion between `user_id` and `remote_identity_id` is enforced at
/// the DB level by the CHECK XOR `chk_team_members_principal`.
#[derive(Clone, Debug, Serialize, Deserialize, DeriveEntityModel)]
#[sea_orm(table_name = "team_members")]
pub struct Model {
    /// Surrogate primary key (RFC 0018).
    #[sea_orm(primary_key, auto_increment = false)]
    pub id: Uuid,
    /// FK → teams.id.
    pub team_id: Uuid,
    /// FK → users.id (`None` for cross-instance memberships).
    pub user_id: Option<Uuid>,
    /// `"owner" | "admin" | "member"` (validated at the service layer).
    pub role: String,
    /// Timestamp when the user joined (or was added to) the team.
    pub joined_at: DateTime<Utc>,
    /// RFC 0008/0018: optional FK to `remote_identities.id` for
    /// cross-instance memberships. Mutually exclusive with `user_id`
    /// (enforced by `chk_team_members_principal`).
    pub remote_identity_id: Option<Uuid>,
}

/// SeaORM relations for the TeamMember entity.
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {
    /// The team this membership belongs to.
    #[sea_orm(
        belongs_to = "super::team::Entity",
        from = "Column::TeamId",
        to = "super::team::Column::Id",
        on_delete = "Cascade"
    )]
    Team,
    /// The user who is a member of the team.
    #[sea_orm(
        belongs_to = "super::user::Entity",
        from = "Column::UserId",
        to = "super::user::Column::Id",
        on_delete = "Cascade"
    )]
    User,
}

impl Related<super::team::Entity> for Entity {
    fn to() -> RelationDef {
        Relation::Team.def()
    }
}

impl Related<super::user::Entity> for Entity {
    fn to() -> RelationDef {
        Relation::User.def()
    }
}

impl ActiveModelBehavior for ActiveModel {}