556 lines
15 KiB
Rust
556 lines
15 KiB
Rust
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_json::Value;
|
|
use std::fmt;
|
|
use std::io::Write;
|
|
use std::str;
|
|
use uuid::Uuid;
|
|
|
|
use crate::schema::sql_types::{
|
|
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,
|
|
}
|
|
|
|
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 fmt::Display for MagicTokenKind {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
write!(f, "{}", self.as_str())
|
|
}
|
|
}
|
|
|
|
impl ToSql<MagicTokenKindSql, Pg> 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 FromSql<MagicTokenKindSql, Pg> for MagicTokenKind {
|
|
fn from_sql(bytes: PgValue<'_>) -> deserialize::Result<Self> {
|
|
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 str::FromStr for MagicTokenKind {
|
|
type Err = &'static str;
|
|
|
|
fn from_str(value: &str) -> Result<Self, Self::Err> {
|
|
match value {
|
|
"email_login" => Ok(MagicTokenKind::EmailLogin),
|
|
"demo_login" => Ok(MagicTokenKind::DemoLogin),
|
|
_ => Err("unsupported magic token kind"),
|
|
}
|
|
}
|
|
}
|
|
|
|
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<Self> {
|
|
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<TenantStatusSql, Pg> 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<TenantStatusSql, Pg> for TenantStatus {
|
|
fn from_sql(bytes: PgValue<'_>) -> deserialize::Result<Self> {
|
|
let value = str::from_utf8(bytes.as_bytes())
|
|
.map_err(|err| Box::<dyn std::error::Error + Send + Sync>::from(err))?;
|
|
TenantStatus::from_str(value).ok_or_else(|| {
|
|
Box::<dyn std::error::Error + Send + Sync>::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<String>,
|
|
pub quickwit_index: Option<String>,
|
|
pub config: Value,
|
|
pub created_at: NaiveDateTime,
|
|
pub updated_at: NaiveDateTime,
|
|
pub status: TenantStatus,
|
|
pub created_by: Option<Uuid>,
|
|
}
|
|
|
|
#[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<u8>,
|
|
pub public_key: Vec<u8>,
|
|
pub credential: serde_json::Value,
|
|
pub sign_count: i64,
|
|
pub transports: Vec<Option<String>>,
|
|
pub aaguid: Option<Uuid>,
|
|
pub nickname: Option<String>,
|
|
pub created_at: NaiveDateTime,
|
|
pub updated_at: NaiveDateTime,
|
|
pub last_used_at: Option<NaiveDateTime>,
|
|
pub revoked_at: Option<NaiveDateTime>,
|
|
pub revoked_by: Option<Uuid>,
|
|
pub revoked_reason: Option<String>,
|
|
}
|
|
|
|
#[derive(Debug, Insertable)]
|
|
#[diesel(table_name = user_passkeys)]
|
|
pub struct NewUserPasskey {
|
|
pub id: Uuid,
|
|
pub user_id: Uuid,
|
|
pub credential_id: Vec<u8>,
|
|
pub public_key: Vec<u8>,
|
|
pub credential: serde_json::Value,
|
|
pub sign_count: i64,
|
|
pub transports: Vec<Option<String>>,
|
|
pub aaguid: Option<Uuid>,
|
|
pub nickname: Option<String>,
|
|
}
|
|
|
|
#[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<Uuid>,
|
|
pub purpose: String,
|
|
pub challenge: Vec<u8>,
|
|
pub state: Vec<u8>,
|
|
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<Uuid>,
|
|
pub purpose: String,
|
|
pub challenge: Vec<u8>,
|
|
pub state: Vec<u8>,
|
|
pub expires_at: NaiveDateTime,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Queryable, Identifiable, Associations)]
|
|
#[diesel(table_name = webdav_tokens)]
|
|
#[diesel(belongs_to(User))]
|
|
#[diesel(belongs_to(Tenant))]
|
|
pub struct WebdavToken {
|
|
pub id: Uuid,
|
|
pub user_id: Uuid,
|
|
pub tenant_id: Uuid,
|
|
pub token_prefix: String,
|
|
pub token_hash: String,
|
|
pub label: Option<String>,
|
|
pub created_at: NaiveDateTime,
|
|
pub last_used_at: Option<NaiveDateTime>,
|
|
pub expires_at: Option<NaiveDateTime>,
|
|
pub revoked_at: Option<NaiveDateTime>,
|
|
}
|
|
|
|
#[derive(Debug, Insertable)]
|
|
#[diesel(table_name = webdav_tokens)]
|
|
pub struct NewWebdavToken {
|
|
pub id: Uuid,
|
|
pub user_id: Uuid,
|
|
pub tenant_id: Uuid,
|
|
pub token_prefix: String,
|
|
pub token_hash: String,
|
|
pub label: Option<String>,
|
|
pub expires_at: Option<NaiveDateTime>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Queryable, Identifiable)]
|
|
#[diesel(table_name = folders)]
|
|
pub struct Folder {
|
|
pub id: Uuid,
|
|
pub name: String,
|
|
pub parent_id: Option<Uuid>,
|
|
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<Uuid>,
|
|
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<String>,
|
|
pub folder_id: Option<Uuid>,
|
|
pub created_at: NaiveDateTime,
|
|
pub updated_at: NaiveDateTime,
|
|
pub deleted_at: Option<NaiveDateTime>,
|
|
pub metadata: serde_json::Value,
|
|
pub issued_at: Option<NaiveDateTime>,
|
|
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<String>,
|
|
pub folder_id: Option<Uuid>,
|
|
pub current_version_id: Uuid,
|
|
pub metadata: serde_json::Value,
|
|
pub issued_at: Option<NaiveDateTime>,
|
|
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<i32>,
|
|
pub used_count: i32,
|
|
pub created_at: NaiveDateTime,
|
|
pub created_by: Option<Uuid>,
|
|
pub last_used_at: Option<NaiveDateTime>,
|
|
}
|
|
|
|
#[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<i32>,
|
|
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<i32>,
|
|
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<String>,
|
|
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<String>,
|
|
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<String>,
|
|
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<Uuid>,
|
|
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<Uuid>,
|
|
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<Uuid>,
|
|
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<Uuid>,
|
|
pub tenant_id: Uuid,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Queryable, Identifiable, Associations)]
|
|
#[diesel(table_name = refresh_tokens)]
|
|
#[diesel(belongs_to(User))]
|
|
pub struct RefreshToken {
|
|
pub id: Uuid,
|
|
pub user_id: Uuid,
|
|
pub token_hash: String,
|
|
pub issued_at: NaiveDateTime,
|
|
pub expires_at: NaiveDateTime,
|
|
pub revoked_at: Option<NaiveDateTime>,
|
|
pub created_at: NaiveDateTime,
|
|
pub updated_at: NaiveDateTime,
|
|
pub tenant_id: Uuid,
|
|
}
|
|
|
|
#[derive(Debug, Insertable)]
|
|
#[diesel(table_name = refresh_tokens)]
|
|
pub struct NewRefreshToken {
|
|
pub id: Uuid,
|
|
pub user_id: Uuid,
|
|
pub token_hash: String,
|
|
pub issued_at: NaiveDateTime,
|
|
pub expires_at: NaiveDateTime,
|
|
pub tenant_id: Uuid,
|
|
}
|