feat: add permission control
This commit is contained in:
@@ -1,7 +1,13 @@
|
||||
use async_trait::async_trait;
|
||||
use sea_orm::entity::prelude::*;
|
||||
use loco_rs::{
|
||||
app::AppContext,
|
||||
model::{ModelError, ModelResult},
|
||||
};
|
||||
use sea_orm::{Set, TransactionTrait, entity::prelude::*};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::subscribers::{self, SEED_SUBSCRIBER};
|
||||
|
||||
#[derive(
|
||||
Clone, Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum, DeriveDisplay, Serialize, Deserialize,
|
||||
)]
|
||||
@@ -17,14 +23,16 @@ pub enum AuthType {
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, DeriveEntityModel)]
|
||||
#[sea_orm(table_name = "auth")]
|
||||
pub struct Model {
|
||||
#[sea_orm(default_expr = "Expr::current_timestamp()")]
|
||||
pub created_at: DateTime,
|
||||
#[sea_orm(default_expr = "Expr::current_timestamp()")]
|
||||
pub updated_at: DateTime,
|
||||
#[sea_orm(primary_key)]
|
||||
pub id: i32,
|
||||
#[sea_orm(unique)]
|
||||
pub pid: String,
|
||||
pub subscriber_id: i32,
|
||||
pub auth_type: AuthType,
|
||||
pub avatar_url: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
@@ -47,3 +55,52 @@ impl Related<super::subscribers::Entity> for Entity {
|
||||
|
||||
#[async_trait]
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
||||
|
||||
impl Model {
|
||||
pub async fn find_by_pid(ctx: &AppContext, pid: &str) -> ModelResult<Self> {
|
||||
let db = &ctx.db;
|
||||
let subscriber_auth = Entity::find()
|
||||
.filter(Column::Pid.eq(pid))
|
||||
.one(db)
|
||||
.await?
|
||||
.ok_or_else(|| ModelError::EntityNotFound)?;
|
||||
Ok(subscriber_auth)
|
||||
}
|
||||
|
||||
pub async fn create_from_oidc(ctx: &AppContext, sub: String) -> ModelResult<Self> {
|
||||
let db = &ctx.db;
|
||||
|
||||
let txn = db.begin().await?;
|
||||
|
||||
let subscriber_id = if let Some(seed_subscriber_id) = Entity::find()
|
||||
.filter(
|
||||
Column::AuthType
|
||||
.eq(AuthType::Basic)
|
||||
.and(Column::Pid.eq(SEED_SUBSCRIBER)),
|
||||
)
|
||||
.one(&txn)
|
||||
.await?
|
||||
.map(|m| m.subscriber_id)
|
||||
{
|
||||
seed_subscriber_id
|
||||
} else {
|
||||
let new_subscriber = subscribers::ActiveModel {
|
||||
..Default::default()
|
||||
};
|
||||
let new_subscriber: subscribers::Model = new_subscriber.save(&txn).await?.try_into()?;
|
||||
|
||||
new_subscriber.id
|
||||
};
|
||||
|
||||
let new_item = ActiveModel {
|
||||
pid: Set(sub),
|
||||
auth_type: Set(AuthType::Oidc),
|
||||
subscriber_id: Set(subscriber_id),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let new_item: Model = new_item.save(&txn).await?.try_into()?;
|
||||
|
||||
Ok(new_item)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use async_graphql::SimpleObject;
|
||||
use async_trait::async_trait;
|
||||
use loco_rs::app::AppContext;
|
||||
use sea_orm::{entity::prelude::*, sea_query::OnConflict, ActiveValue, FromJsonQueryResult};
|
||||
use sea_orm::{ActiveValue, FromJsonQueryResult, entity::prelude::*, sea_query::OnConflict};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::subscription_bangumi;
|
||||
@@ -9,7 +9,6 @@ use super::subscription_bangumi;
|
||||
#[derive(
|
||||
Clone, Debug, PartialEq, Eq, Serialize, Deserialize, FromJsonQueryResult, SimpleObject,
|
||||
)]
|
||||
#[graphql(name = "BangumiFilter")]
|
||||
pub struct BangumiFilter {
|
||||
pub name: Option<Vec<String>>,
|
||||
pub group: Option<Vec<String>>,
|
||||
@@ -18,7 +17,6 @@ pub struct BangumiFilter {
|
||||
#[derive(
|
||||
Clone, Debug, PartialEq, Eq, Serialize, Deserialize, FromJsonQueryResult, SimpleObject,
|
||||
)]
|
||||
#[graphql(name = "BangumiExtra")]
|
||||
pub struct BangumiExtra {
|
||||
pub name_zh: Option<String>,
|
||||
pub s_name_zh: Option<String>,
|
||||
@@ -30,14 +28,14 @@ pub struct BangumiExtra {
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize, SimpleObject)]
|
||||
#[sea_orm(table_name = "bangumi")]
|
||||
#[graphql(name = "Bangumi")]
|
||||
pub struct Model {
|
||||
#[sea_orm(default_expr = "Expr::current_timestamp()")]
|
||||
pub created_at: DateTime,
|
||||
#[sea_orm(default_expr = "Expr::current_timestamp()")]
|
||||
pub updated_at: DateTime,
|
||||
#[sea_orm(primary_key)]
|
||||
pub id: i32,
|
||||
pub mikan_bangumi_id: Option<String>,
|
||||
#[graphql(default_with = "default_subscriber_id")]
|
||||
pub subscriber_id: i32,
|
||||
pub display_name: String,
|
||||
pub raw_name: String,
|
||||
|
||||
@@ -22,9 +22,9 @@ pub enum DownloaderCategory {
|
||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
|
||||
#[sea_orm(table_name = "downloaders")]
|
||||
pub struct Model {
|
||||
#[sea_orm(column_type = "Timestamp")]
|
||||
#[sea_orm(default_expr = "Expr::current_timestamp()")]
|
||||
pub created_at: DateTime,
|
||||
#[sea_orm(column_type = "Timestamp")]
|
||||
#[sea_orm(default_expr = "Expr::current_timestamp()")]
|
||||
pub updated_at: DateTime,
|
||||
#[sea_orm(primary_key)]
|
||||
pub id: i32,
|
||||
|
||||
@@ -38,7 +38,9 @@ pub enum DownloadMime {
|
||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
|
||||
#[sea_orm(table_name = "downloads")]
|
||||
pub struct Model {
|
||||
#[sea_orm(default_expr = "Expr::current_timestamp()")]
|
||||
pub created_at: DateTime,
|
||||
#[sea_orm(default_expr = "Expr::current_timestamp()")]
|
||||
pub updated_at: DateTime,
|
||||
#[sea_orm(primary_key)]
|
||||
pub id: i32,
|
||||
|
||||
@@ -2,14 +2,14 @@ use std::sync::Arc;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use loco_rs::app::AppContext;
|
||||
use sea_orm::{entity::prelude::*, sea_query::OnConflict, ActiveValue, FromJsonQueryResult};
|
||||
use sea_orm::{ActiveValue, FromJsonQueryResult, entity::prelude::*, sea_query::OnConflict};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::{bangumi, query::InsertManyReturningExt, subscription_episode};
|
||||
use crate::{
|
||||
app::AppContextExt,
|
||||
extract::{
|
||||
mikan::{build_mikan_episode_homepage, MikanEpisodeMeta},
|
||||
mikan::{MikanEpisodeMeta, build_mikan_episode_homepage},
|
||||
rawname::parse_episode_meta_from_raw_name,
|
||||
},
|
||||
};
|
||||
@@ -27,7 +27,9 @@ pub struct EpisodeExtra {
|
||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
|
||||
#[sea_orm(table_name = "episodes")]
|
||||
pub struct Model {
|
||||
#[sea_orm(default_expr = "Expr::current_timestamp()")]
|
||||
pub created_at: DateTime,
|
||||
#[sea_orm(default_expr = "Expr::current_timestamp()")]
|
||||
pub updated_at: DateTime,
|
||||
#[sea_orm(primary_key)]
|
||||
pub id: i32,
|
||||
@@ -135,6 +137,7 @@ pub struct MikanEpsiodeCreation {
|
||||
impl Model {
|
||||
pub async fn add_episodes(
|
||||
ctx: &AppContext,
|
||||
subscriber_id: i32,
|
||||
subscription_id: i32,
|
||||
creations: impl IntoIterator<Item = MikanEpsiodeCreation>,
|
||||
) -> color_eyre::eyre::Result<()> {
|
||||
@@ -162,6 +165,7 @@ impl Model {
|
||||
|
||||
let insert_subscription_episode_links = inserted_episodes.into_iter().map(|episode_id| {
|
||||
subscription_episode::ActiveModel::from_subscription_and_episode(
|
||||
subscriber_id,
|
||||
subscription_id,
|
||||
episode_id,
|
||||
)
|
||||
|
||||
@@ -4,7 +4,7 @@ use loco_rs::{
|
||||
app::AppContext,
|
||||
model::{ModelError, ModelResult},
|
||||
};
|
||||
use sea_orm::{entity::prelude::*, ActiveValue, FromJsonQueryResult, TransactionTrait};
|
||||
use sea_orm::{ActiveValue, FromJsonQueryResult, TransactionTrait, entity::prelude::*};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
pub const SEED_SUBSCRIBER: &str = "konobangu";
|
||||
@@ -16,15 +16,15 @@ pub struct SubscriberBangumiConfig {
|
||||
pub leading_group_tag: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
|
||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize, SimpleObject)]
|
||||
#[sea_orm(table_name = "subscribers")]
|
||||
pub struct Model {
|
||||
#[sea_orm(default_expr = "Expr::current_timestamp()")]
|
||||
pub created_at: DateTime,
|
||||
#[sea_orm(default_expr = "Expr::current_timestamp()")]
|
||||
pub updated_at: DateTime,
|
||||
#[sea_orm(primary_key)]
|
||||
pub id: i32,
|
||||
#[sea_orm(unique)]
|
||||
pub pid: String,
|
||||
pub display_name: String,
|
||||
pub bangumi_conf: Option<SubscriberBangumiConfig>,
|
||||
}
|
||||
@@ -91,59 +91,22 @@ pub struct SubscriberIdParams {
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl ActiveModelBehavior for ActiveModel {
|
||||
async fn before_save<C>(self, _db: &C, insert: bool) -> Result<Self, DbErr>
|
||||
where
|
||||
C: ConnectionTrait,
|
||||
{
|
||||
if insert {
|
||||
let mut this = self;
|
||||
this.pid = ActiveValue::Set(Uuid::new_v4().to_string());
|
||||
Ok(this)
|
||||
} else {
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
}
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
||||
|
||||
impl Model {
|
||||
pub async fn find_by_pid(ctx: &AppContext, pid: &str) -> ModelResult<Self> {
|
||||
let db = &ctx.db;
|
||||
let parse_uuid = Uuid::parse_str(pid).map_err(|e| ModelError::Any(e.into()))?;
|
||||
let subscriber = Entity::find()
|
||||
.filter(Column::Pid.eq(parse_uuid))
|
||||
.one(db)
|
||||
.await?;
|
||||
subscriber.ok_or_else(|| ModelError::EntityNotFound)
|
||||
pub async fn find_seed_subscriber_id(ctx: &AppContext) -> ModelResult<i32> {
|
||||
let subscriber_auth = crate::models::auth::Model::find_by_pid(ctx, SEED_SUBSCRIBER).await?;
|
||||
Ok(subscriber_auth.subscriber_id)
|
||||
}
|
||||
|
||||
pub async fn find_by_id(ctx: &AppContext, id: i32) -> ModelResult<Self> {
|
||||
let db = &ctx.db;
|
||||
|
||||
let subscriber = Entity::find_by_id(id).one(db).await?;
|
||||
subscriber.ok_or_else(|| ModelError::EntityNotFound)
|
||||
}
|
||||
|
||||
pub async fn find_pid_by_id_with_cache(
|
||||
ctx: &AppContext,
|
||||
id: i32,
|
||||
) -> color_eyre::eyre::Result<String> {
|
||||
let db = &ctx.db;
|
||||
let cache = &ctx.cache;
|
||||
let pid = cache
|
||||
.get_or_insert(&format!("subscriber-id2pid::{}", id), async {
|
||||
let subscriber = Entity::find_by_id(id)
|
||||
.one(db)
|
||||
.await?
|
||||
.ok_or_else(|| loco_rs::Error::string(&format!("No such pid for id {}", id)))?;
|
||||
Ok(subscriber.pid)
|
||||
})
|
||||
.await?;
|
||||
Ok(pid)
|
||||
}
|
||||
|
||||
pub async fn find_root(ctx: &AppContext) -> ModelResult<Self> {
|
||||
Self::find_by_pid(ctx, SEED_SUBSCRIBER).await
|
||||
let subscriber = Entity::find_by_id(id)
|
||||
.one(db)
|
||||
.await?
|
||||
.ok_or_else(|| ModelError::EntityNotFound)?;
|
||||
Ok(subscriber)
|
||||
}
|
||||
|
||||
pub async fn create_root(ctx: &AppContext) -> ModelResult<Self> {
|
||||
@@ -152,7 +115,6 @@ impl Model {
|
||||
|
||||
let user = ActiveModel {
|
||||
display_name: ActiveValue::set(SEED_SUBSCRIBER.to_string()),
|
||||
pid: ActiveValue::set(SEED_SUBSCRIBER.to_string()),
|
||||
..Default::default()
|
||||
}
|
||||
.insert(&txn)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use async_trait::async_trait;
|
||||
use sea_orm::{entity::prelude::*, ActiveValue};
|
||||
use sea_orm::{ActiveValue, entity::prelude::*};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
|
||||
@@ -7,6 +7,7 @@ use serde::{Deserialize, Serialize};
|
||||
pub struct Model {
|
||||
#[sea_orm(primary_key)]
|
||||
pub id: i32,
|
||||
pub subscriber_id: i32,
|
||||
pub subscription_id: i32,
|
||||
pub bangumi_id: i32,
|
||||
}
|
||||
@@ -55,8 +56,13 @@ pub enum RelatedEntity {
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
||||
|
||||
impl ActiveModel {
|
||||
pub fn from_subscription_and_bangumi(subscription_id: i32, bangumi_id: i32) -> Self {
|
||||
pub fn from_subscription_and_bangumi(
|
||||
subscriber_id: i32,
|
||||
subscription_id: i32,
|
||||
bangumi_id: i32,
|
||||
) -> Self {
|
||||
Self {
|
||||
subscriber_id: ActiveValue::Set(subscriber_id),
|
||||
subscription_id: ActiveValue::Set(subscription_id),
|
||||
bangumi_id: ActiveValue::Set(bangumi_id),
|
||||
..Default::default()
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use async_trait::async_trait;
|
||||
use sea_orm::{entity::prelude::*, ActiveValue};
|
||||
use sea_orm::{ActiveValue, entity::prelude::*};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
|
||||
@@ -7,6 +7,7 @@ use serde::{Deserialize, Serialize};
|
||||
pub struct Model {
|
||||
#[sea_orm(primary_key)]
|
||||
pub id: i32,
|
||||
pub subscriber_id: i32,
|
||||
pub subscription_id: i32,
|
||||
pub episode_id: i32,
|
||||
}
|
||||
@@ -55,8 +56,13 @@ pub enum RelatedEntity {
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
||||
|
||||
impl ActiveModel {
|
||||
pub fn from_subscription_and_episode(subscription_id: i32, episode_id: i32) -> Self {
|
||||
pub fn from_subscription_and_episode(
|
||||
subscriber_id: i32,
|
||||
subscription_id: i32,
|
||||
episode_id: i32,
|
||||
) -> Self {
|
||||
Self {
|
||||
subscriber_id: ActiveValue::Set(subscriber_id),
|
||||
subscription_id: ActiveValue::Set(subscription_id),
|
||||
episode_id: ActiveValue::Set(episode_id),
|
||||
..Default::default()
|
||||
|
||||
@@ -3,7 +3,7 @@ use std::{collections::HashSet, sync::Arc};
|
||||
use async_trait::async_trait;
|
||||
use itertools::Itertools;
|
||||
use loco_rs::app::AppContext;
|
||||
use sea_orm::{entity::prelude::*, ActiveValue};
|
||||
use sea_orm::{ActiveValue, entity::prelude::*};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::{bangumi, episodes, query::filter_values_in};
|
||||
@@ -15,8 +15,8 @@ use crate::{
|
||||
parse_mikan_bangumi_meta_from_mikan_homepage,
|
||||
parse_mikan_episode_meta_from_mikan_homepage, parse_mikan_rss_channel_from_rss_link,
|
||||
web_parser::{
|
||||
parse_mikan_bangumi_poster_from_origin_poster_src_with_cache,
|
||||
MikanBangumiPosterMeta,
|
||||
parse_mikan_bangumi_poster_from_origin_poster_src_with_cache,
|
||||
},
|
||||
},
|
||||
rawname::extract_season_from_title_body,
|
||||
@@ -43,9 +43,9 @@ pub enum SubscriptionCategory {
|
||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
|
||||
#[sea_orm(table_name = "subscriptions")]
|
||||
pub struct Model {
|
||||
#[sea_orm(column_type = "Timestamp")]
|
||||
#[sea_orm(default_expr = "Expr::current_timestamp()")]
|
||||
pub created_at: DateTime,
|
||||
#[sea_orm(column_type = "Timestamp")]
|
||||
#[sea_orm(default_expr = "Expr::current_timestamp()")]
|
||||
pub updated_at: DateTime,
|
||||
#[sea_orm(primary_key)]
|
||||
pub id: i32,
|
||||
@@ -325,6 +325,7 @@ impl Model {
|
||||
);
|
||||
episodes::Model::add_episodes(
|
||||
ctx,
|
||||
self.subscriber_id,
|
||||
self.id,
|
||||
new_ep_metas.into_iter().map(|item| MikanEpsiodeCreation {
|
||||
episode: item,
|
||||
|
||||
Reference in New Issue
Block a user