use chrono::NaiveDateTime; use diesel::deserialize::FromSql; use diesel::pg::{Pg, PgValue}; use diesel::prelude::*; use diesel::serialize::{IsNull, Output, ToSql}; use diesel::{deserialize, serialize, AsExpression, FromSqlRow}; use serde::{Deserialize, Serialize}; use serde_json::Value; use std::fmt; use std::io::Write; use std::str; use uuid::Uuid; use utoipa::ToSchema; use crate::schema::sql_types::{ ApiTokenCapability as ApiTokenCapabilitySql, MagicTokenKind as MagicTokenKindSql, TenantStatus as TenantStatusSql, }; use crate::schema::*; #[derive(Debug, Clone, Queryable, Identifiable, Associations)] #[diesel(table_name = user_memberships)] #[diesel(belongs_to(User, foreign_key = user_id))] #[diesel(belongs_to(Tenant, foreign_key = tenant_id))] pub struct UserMembership { pub id: Uuid, pub user_id: Uuid, pub tenant_id: Uuid, pub created_at: NaiveDateTime, pub updated_at: NaiveDateTime, } #[derive(Debug, Insertable)] #[diesel(table_name = user_memberships)] pub struct NewUserMembership { pub id: Uuid, pub user_id: Uuid, pub tenant_id: Uuid, } #[derive(Debug, Clone, Copy, PartialEq, Eq, AsExpression, FromSqlRow)] #[diesel(sql_type = TenantStatusSql)] pub enum TenantStatus { Creating, Active, Suspended, Deleting, Error, } #[derive(Debug, Clone, Copy, PartialEq, Eq, AsExpression, FromSqlRow)] #[diesel(sql_type = MagicTokenKindSql)] pub enum MagicTokenKind { EmailLogin, DemoLogin, } #[derive( Debug, Clone, Copy, PartialEq, Eq, AsExpression, FromSqlRow, Serialize, Deserialize, ToSchema, )] #[diesel(sql_type = ApiTokenCapabilitySql)] #[serde(rename_all = "snake_case")] pub enum ApiTokenCapability { Api, Webdav, } impl MagicTokenKind { pub fn as_str(&self) -> &'static str { match self { MagicTokenKind::EmailLogin => "email_login", MagicTokenKind::DemoLogin => "demo_login", } } pub fn variants() -> &'static [&'static str] { &["email_login", "demo_login"] } } impl ApiTokenCapability { pub fn as_str(&self) -> &'static str { match self { ApiTokenCapability::Api => "api", ApiTokenCapability::Webdav => "webdav", } } pub fn variants() -> &'static [&'static str] { &["api", "webdav"] } } impl fmt::Display for MagicTokenKind { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.as_str()) } } impl fmt::Display for ApiTokenCapability { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.as_str()) } } impl ToSql for MagicTokenKind { fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>) -> serialize::Result { out.write_all(self.as_str().as_bytes())?; Ok(IsNull::No) } } impl ToSql for ApiTokenCapability { fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>) -> serialize::Result { out.write_all(self.as_str().as_bytes())?; Ok(IsNull::No) } } impl FromSql for MagicTokenKind { fn from_sql(bytes: PgValue<'_>) -> deserialize::Result { match std::str::from_utf8(bytes.as_bytes())? { "email_login" => Ok(MagicTokenKind::EmailLogin), "demo_login" => Ok(MagicTokenKind::DemoLogin), other => Err(Box::new(std::io::Error::new( std::io::ErrorKind::InvalidData, format!("invalid magic_token_kind '{other}'"), ))), } } } impl FromSql for ApiTokenCapability { fn from_sql(bytes: PgValue<'_>) -> deserialize::Result { match std::str::from_utf8(bytes.as_bytes())? { "api" => Ok(ApiTokenCapability::Api), "webdav" => Ok(ApiTokenCapability::Webdav), other => Err(Box::new(std::io::Error::new( std::io::ErrorKind::InvalidData, format!("invalid api_token_capability '{other}'"), ))), } } } impl str::FromStr for MagicTokenKind { type Err = &'static str; fn from_str(value: &str) -> Result { match value { "email_login" => Ok(MagicTokenKind::EmailLogin), "demo_login" => Ok(MagicTokenKind::DemoLogin), _ => Err("unsupported magic token kind"), } } } impl str::FromStr for ApiTokenCapability { type Err = &'static str; fn from_str(value: &str) -> Result { match value { "api" => Ok(ApiTokenCapability::Api), "webdav" => Ok(ApiTokenCapability::Webdav), _ => Err("unsupported api token capability"), } } } impl TenantStatus { pub fn as_str(&self) -> &'static str { match self { TenantStatus::Creating => "creating", TenantStatus::Active => "active", TenantStatus::Suspended => "suspended", TenantStatus::Deleting => "deleting", TenantStatus::Error => "error", } } pub fn from_str(value: &str) -> Option { match value { "creating" => Some(TenantStatus::Creating), "active" => Some(TenantStatus::Active), "suspended" => Some(TenantStatus::Suspended), "deleting" => Some(TenantStatus::Deleting), "error" => Some(TenantStatus::Error), _ => None, } } } impl fmt::Display for TenantStatus { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.as_str()) } } impl ToSql for TenantStatus { fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>) -> serialize::Result { out.write_all(self.as_str().as_bytes())?; Ok(IsNull::No) } } impl FromSql for TenantStatus { fn from_sql(bytes: PgValue<'_>) -> deserialize::Result { let value = str::from_utf8(bytes.as_bytes()) .map_err(|err| Box::::from(err))?; TenantStatus::from_str(value).ok_or_else(|| { Box::::from(std::io::Error::new( std::io::ErrorKind::InvalidData, format!("invalid tenant status '{value}'"), )) }) } } #[derive(Debug, Clone, Queryable, Identifiable)] #[diesel(table_name = tenants)] #[diesel(primary_key(id))] pub struct Tenant { pub id: Uuid, pub name: String, pub storage_root: Option, pub quickwit_index: Option, pub config: Value, pub created_at: NaiveDateTime, pub updated_at: NaiveDateTime, pub status: TenantStatus, pub created_by: Option, } #[derive(Debug, Clone, Queryable, Identifiable)] #[diesel(table_name = users)] pub struct User { pub id: Uuid, pub username: String, pub created_at: NaiveDateTime, pub updated_at: NaiveDateTime, } #[derive(Debug, Insertable)] #[diesel(table_name = users)] pub struct NewUser { pub id: Uuid, pub username: String, } #[derive(Debug, Clone, Queryable, Identifiable, Associations, Selectable)] #[diesel(table_name = user_passkeys)] #[diesel(belongs_to(User))] pub struct UserPasskey { pub id: Uuid, pub user_id: Uuid, pub credential_id: Vec, pub public_key: Vec, pub credential: serde_json::Value, pub sign_count: i64, pub transports: Vec>, pub aaguid: Option, pub nickname: Option, pub created_at: NaiveDateTime, pub updated_at: NaiveDateTime, pub last_used_at: Option, pub revoked_at: Option, pub revoked_by: Option, pub revoked_reason: Option, } #[derive(Debug, Insertable)] #[diesel(table_name = user_passkeys)] pub struct NewUserPasskey { pub id: Uuid, pub user_id: Uuid, pub credential_id: Vec, pub public_key: Vec, pub credential: serde_json::Value, pub sign_count: i64, pub transports: Vec>, pub aaguid: Option, pub nickname: Option, } #[derive(Debug, Clone, Queryable, Identifiable, Associations)] #[diesel(table_name = webauthn_challenges)] #[diesel(belongs_to(User))] pub struct WebauthnChallenge { pub id: Uuid, pub user_id: Option, pub purpose: String, pub challenge: Vec, pub state: Vec, pub created_at: NaiveDateTime, pub expires_at: NaiveDateTime, } #[derive(Debug, Insertable)] #[diesel(table_name = webauthn_challenges)] pub struct NewWebauthnChallenge { pub id: Uuid, pub user_id: Option, pub purpose: String, pub challenge: Vec, pub state: Vec, pub expires_at: NaiveDateTime, } #[derive(Debug, Clone, Queryable, Identifiable, Associations)] #[diesel(table_name = api_tokens)] #[diesel(belongs_to(User))] #[diesel(belongs_to(Tenant))] pub struct ApiToken { pub id: Uuid, pub user_id: Uuid, pub tenant_id: Uuid, pub token_prefix: String, pub token_hash: String, pub label: Option, pub created_at: NaiveDateTime, pub last_used_at: Option, pub expires_at: Option, pub revoked_at: Option, pub capabilities: Vec, } #[derive(Debug, Insertable)] #[diesel(table_name = api_tokens)] pub struct NewApiToken { pub id: Uuid, pub user_id: Uuid, pub tenant_id: Uuid, pub token_prefix: String, pub token_hash: String, pub label: Option, pub expires_at: Option, pub capabilities: Vec, } #[derive(Debug, Clone, Queryable, Identifiable)] #[diesel(table_name = folders)] pub struct Folder { pub id: Uuid, pub name: String, pub parent_id: Option, pub created_at: NaiveDateTime, pub updated_at: NaiveDateTime, pub tenant_id: Uuid, } #[derive(Debug, Insertable)] #[diesel(table_name = folders)] pub struct NewFolder { pub id: Uuid, pub name: String, pub parent_id: Option, pub tenant_id: Uuid, } #[derive(Debug, Clone, Queryable, Identifiable, Associations)] #[diesel(table_name = documents)] #[diesel(belongs_to(Folder, foreign_key = folder_id))] pub struct Document { pub id: Uuid, pub filename: String, pub original_name: String, pub content_type: Option, pub folder_id: Option, pub created_at: NaiveDateTime, pub updated_at: NaiveDateTime, pub deleted_at: Option, pub metadata: serde_json::Value, pub issued_at: Option, pub title: String, pub current_version_id: Uuid, pub tenant_id: Uuid, } #[derive(Debug, Insertable)] #[diesel(table_name = documents)] pub struct NewDocument { pub id: Uuid, pub filename: String, pub original_name: String, pub content_type: Option, pub folder_id: Option, pub current_version_id: Uuid, pub metadata: serde_json::Value, pub issued_at: Option, pub title: String, pub tenant_id: Uuid, } #[derive(Debug, Clone, Queryable, Identifiable, Insertable)] #[diesel(table_name = magic_tokens)] pub struct MagicToken { pub id: Uuid, pub user_id: Uuid, pub kind: MagicTokenKind, pub token_hash: String, pub metadata: serde_json::Value, pub expires_at: NaiveDateTime, pub max_uses: Option, pub used_count: i32, pub created_at: NaiveDateTime, pub created_by: Option, pub last_used_at: Option, } #[derive(Debug, Clone, Queryable, Identifiable, Associations)] #[diesel(table_name = document_versions)] #[diesel(belongs_to(Document))] pub struct DocumentVersion { pub id: Uuid, pub document_id: Uuid, pub version_number: i32, pub s3_key: String, pub size_bytes: i64, pub checksum: String, pub created_at: NaiveDateTime, pub metadata: serde_json::Value, pub tenant_id: Uuid, } #[derive(Debug, Insertable)] #[diesel(table_name = document_versions)] pub struct NewDocumentVersion { pub id: Uuid, pub document_id: Uuid, pub version_number: i32, pub s3_key: String, pub size_bytes: i64, pub checksum: String, pub metadata: serde_json::Value, pub tenant_id: Uuid, } #[derive(Debug, Clone, Queryable, Identifiable, Associations)] #[diesel(table_name = document_assets)] #[diesel(belongs_to(DocumentVersion, foreign_key = document_version_id))] pub struct DocumentAsset { pub id: Uuid, pub document_version_id: Uuid, pub asset_type: String, pub mime_type: String, pub metadata: serde_json::Value, pub created_at: NaiveDateTime, pub cardinality: Option, pub tenant_id: Uuid, } #[derive(Debug, Insertable)] #[diesel(table_name = document_assets)] pub struct NewDocumentAsset { pub id: Uuid, pub document_version_id: Uuid, pub asset_type: String, pub mime_type: String, pub metadata: serde_json::Value, pub cardinality: Option, pub tenant_id: Uuid, } #[derive(Debug, Clone, Queryable, Identifiable, Associations)] #[diesel(table_name = document_asset_objects)] #[diesel(belongs_to(DocumentAsset, foreign_key = asset_id))] pub struct DocumentAssetObject { pub id: Uuid, pub asset_id: Uuid, pub ordinal: i32, pub s3_key: String, pub metadata: serde_json::Value, pub tenant_id: Uuid, } #[derive(Debug, Insertable)] #[diesel(table_name = document_asset_objects)] pub struct NewDocumentAssetObject { pub id: Uuid, pub asset_id: Uuid, pub ordinal: i32, pub s3_key: String, pub metadata: serde_json::Value, pub tenant_id: Uuid, } #[derive(Debug, Clone, Queryable, Identifiable)] #[diesel(table_name = jobs)] pub struct Job { pub id: Uuid, pub job_type: String, pub payload: serde_json::Value, pub status: String, pub attempts: i32, pub run_after: NaiveDateTime, pub last_error: Option, pub created_at: NaiveDateTime, pub updated_at: NaiveDateTime, pub tenant_id: Uuid, } #[derive(Debug, Insertable)] #[diesel(table_name = jobs)] pub struct NewJob { pub id: Uuid, pub job_type: String, pub payload: serde_json::Value, pub status: String, pub run_after: NaiveDateTime, pub tenant_id: Uuid, } #[derive(Debug, Clone, Queryable, Identifiable)] #[diesel(table_name = tags)] pub struct Tag { pub id: Uuid, pub label: String, pub color: Option, pub created_at: NaiveDateTime, pub tenant_id: Uuid, } #[derive(Debug, Insertable)] #[diesel(table_name = tags)] pub struct NewTag { pub id: Uuid, pub label: String, pub color: Option, pub tenant_id: Uuid, } #[allow(dead_code)] #[derive(Debug, Clone, Queryable, Associations)] #[diesel(table_name = document_tags)] #[diesel(belongs_to(Document))] #[diesel(belongs_to(Tag))] #[diesel(primary_key(document_id, tag_id))] pub struct DocumentTag { pub document_id: Uuid, pub tag_id: Uuid, pub assigned_at: NaiveDateTime, pub assigned_by: Option, pub tenant_id: Uuid, } #[derive(Debug, Insertable)] #[diesel(table_name = document_tags)] pub struct NewDocumentTag { pub document_id: Uuid, pub tag_id: Uuid, pub assigned_by: Option, pub tenant_id: Uuid, } #[derive(Debug, Clone, Queryable, Identifiable)] #[diesel(table_name = correspondents)] pub struct Correspondent { pub id: Uuid, pub name: String, pub metadata: serde_json::Value, pub created_at: NaiveDateTime, pub updated_at: NaiveDateTime, pub tenant_id: Uuid, } #[derive(Debug, Insertable)] #[diesel(table_name = correspondents)] pub struct NewCorrespondent { pub id: Uuid, pub name: String, pub metadata: serde_json::Value, pub tenant_id: Uuid, } #[derive(Debug, Clone, Queryable, Associations)] #[diesel(table_name = document_correspondents)] #[diesel(belongs_to(Document))] #[diesel(belongs_to(Correspondent))] #[diesel(primary_key(document_id, correspondent_id))] pub struct DocumentCorrespondent { pub document_id: Uuid, pub correspondent_id: Uuid, pub assigned_at: NaiveDateTime, pub assigned_by: Option, pub tenant_id: Uuid, } #[derive(Debug, Insertable)] #[diesel(table_name = document_correspondents)] pub struct NewDocumentCorrespondent { pub document_id: Uuid, pub correspondent_id: Uuid, pub assigned_by: Option, pub tenant_id: Uuid, } #[derive(Debug, Clone, Queryable, Identifiable, Associations)] #[diesel(table_name = user_sessions)] #[diesel(belongs_to(User))] pub struct UserSession { pub id: Uuid, pub user_id: Uuid, pub token_hash: String, pub issued_at: NaiveDateTime, pub expires_at: NaiveDateTime, pub revoked_at: Option, pub created_at: NaiveDateTime, pub updated_at: NaiveDateTime, pub tenant_id: Uuid, } #[derive(Debug, Insertable)] #[diesel(table_name = user_sessions)] pub struct NewUserSession { pub id: Uuid, pub user_id: Uuid, pub token_hash: String, pub issued_at: NaiveDateTime, pub expires_at: NaiveDateTime, pub tenant_id: Uuid, }