konobangu/apps/recorder/src/models/credential_3rd.rs

183 lines
5.4 KiB
Rust

use async_trait::async_trait;
use sea_orm::{ActiveValue, prelude::*};
use serde::{Deserialize, Serialize};
use crate::{
app::AppContextTrait,
crypto::UserPassCredential,
errors::{RecorderError, RecorderResult},
};
#[derive(
Debug, Clone, PartialEq, Eq, EnumIter, DeriveActiveEnum, DeriveDisplay, Serialize, Deserialize,
)]
#[sea_orm(
rs_type = "String",
db_type = "Enum",
enum_name = "credential_3rd_type"
)]
#[serde(rename_all = "snake_case")]
pub enum Credential3rdType {
#[sea_orm(string_value = "mikan")]
Mikan,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, DeriveEntityModel)]
#[sea_orm(table_name = "credential3rd")]
pub struct Model {
#[sea_orm(default_expr = "Expr::current_timestamp()")]
pub created_at: DateTimeUtc,
#[sea_orm(default_expr = "Expr::current_timestamp()")]
pub updated_at: DateTimeUtc,
#[sea_orm(primary_key)]
pub id: i32,
pub subscriber_id: i32,
pub credential_type: Credential3rdType,
pub cookies: Option<String>,
pub username: Option<String>,
pub password: Option<String>,
pub user_agent: Option<String>,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {
#[sea_orm(
belongs_to = "super::subscribers::Entity",
from = "Column::SubscriberId",
to = "super::subscribers::Column::Id",
on_update = "Cascade",
on_delete = "Cascade"
)]
Subscriber,
#[sea_orm(has_many = "super::subscriptions::Entity")]
Subscription,
}
impl Related<super::subscribers::Entity> for Entity {
fn to() -> RelationDef {
Relation::Subscriber.def()
}
}
impl Related<super::subscriptions::Entity> for Entity {
fn to() -> RelationDef {
Relation::Subscription.def()
}
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelatedEntity)]
pub enum RelatedEntity {
#[sea_orm(entity = "super::subscribers::Entity")]
Subscriber,
#[sea_orm(entity = "super::subscriptions::Entity")]
Subscription,
}
#[async_trait]
impl ActiveModelBehavior for ActiveModel {}
impl ActiveModel {
pub async fn try_encrypt(mut self, ctx: &dyn AppContextTrait) -> RecorderResult<Self> {
let crypto = ctx.crypto();
if let ActiveValue::Set(Some(username)) = self.username {
let username_enc = crypto.encrypt_string(username)?;
self.username = ActiveValue::Set(Some(username_enc));
}
if let ActiveValue::Set(Some(password)) = self.password {
let password_enc = crypto.encrypt_string(password)?;
self.password = ActiveValue::Set(Some(password_enc));
}
if let ActiveValue::Set(Some(cookies)) = self.cookies {
let cookies_enc = crypto.encrypt_string(cookies)?;
self.cookies = ActiveValue::Set(Some(cookies_enc));
}
Ok(self)
}
}
impl Model {
pub async fn find_by_id_and_subscriber_id(
ctx: &dyn AppContextTrait,
id: i32,
subscriber_id: i32,
) -> RecorderResult<Option<Self>> {
let db = ctx.db();
let credential = Entity::find()
.filter(Column::Id.eq(id))
.filter(Column::SubscriberId.eq(subscriber_id))
.one(db)
.await?;
Ok(credential)
}
pub fn try_into_userpass_credential(
self,
ctx: &dyn AppContextTrait,
) -> RecorderResult<UserPassCredential> {
let crypto = ctx.crypto();
let username_enc = self
.username
.ok_or_else(|| RecorderError::Credential3rdError {
message: "UserPassCredential username is required".to_string(),
source: None.into(),
})?;
let username: String = crypto.decrypt_string(&username_enc)?;
let password_enc = self
.password
.ok_or_else(|| RecorderError::Credential3rdError {
message: "UserPassCredential password is required".to_string(),
source: None.into(),
})?;
let password: String = crypto.decrypt_string(&password_enc)?;
let cookies: Option<String> = if let Some(cookies_enc) = self.cookies {
let cookies = crypto.decrypt_string(&cookies_enc)?;
Some(cookies)
} else {
None
};
Ok(UserPassCredential {
username,
password,
cookies,
user_agent: self.user_agent,
})
}
pub async fn check_available(self, ctx: &dyn AppContextTrait) -> RecorderResult<bool> {
let credential_id = self.id;
let subscriber_id = self.subscriber_id;
match self.credential_type {
Credential3rdType::Mikan => {
let mikan_client = {
let userpass_credential: UserPassCredential =
self.try_into_userpass_credential(ctx)?;
ctx.mikan()
.fork_with_userpass_credential(userpass_credential)
.await?
};
let mut has_login = mikan_client.has_login().await?;
if !has_login {
mikan_client.login().await?;
has_login = true;
}
if has_login {
mikan_client
.sync_credential_cookies(ctx, credential_id, subscriber_id)
.await?;
}
Ok(has_login)
}
}
}
}