refactor: fix database
This commit is contained in:
parent
70932900cd
commit
caaa5dc0cc
198
Cargo.lock
generated
198
Cargo.lock
generated
@ -2,6 +2,16 @@
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "Inflector"
|
||||
version = "0.11.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"regex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "addr2line"
|
||||
version = "0.24.2"
|
||||
@ -165,6 +175,12 @@ version = "0.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
|
||||
|
||||
[[package]]
|
||||
name = "ascii_utils"
|
||||
version = "0.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "71938f30533e4d95a6d17aa530939da3842c2ab6f4f84b9dae68447e4129f74a"
|
||||
|
||||
[[package]]
|
||||
name = "assert-json-diff"
|
||||
version = "2.0.2"
|
||||
@ -191,6 +207,98 @@ dependencies = [
|
||||
"zstd-safe",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-graphql"
|
||||
version = "7.0.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "59fd6bd734afb8b6e4d0f84a3e77305ce0a7ccc60d70f6001cb5e1c3f38d8ff1"
|
||||
dependencies = [
|
||||
"async-graphql-derive",
|
||||
"async-graphql-parser",
|
||||
"async-graphql-value",
|
||||
"async-stream",
|
||||
"async-trait",
|
||||
"base64 0.22.1",
|
||||
"bytes",
|
||||
"fast_chemail",
|
||||
"fnv",
|
||||
"futures-timer",
|
||||
"futures-util",
|
||||
"handlebars",
|
||||
"http 1.2.0",
|
||||
"indexmap 2.7.0",
|
||||
"mime",
|
||||
"multer",
|
||||
"num-traits",
|
||||
"pin-project-lite",
|
||||
"regex",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_urlencoded",
|
||||
"static_assertions_next",
|
||||
"tempfile",
|
||||
"thiserror 1.0.69",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-graphql-axum"
|
||||
version = "7.0.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec8c1bb47161c37286e40e2fa58055e97b2a2b6cf1022a6686967e10636fa5d7"
|
||||
dependencies = [
|
||||
"async-graphql",
|
||||
"async-trait",
|
||||
"axum",
|
||||
"bytes",
|
||||
"futures-util",
|
||||
"serde_json",
|
||||
"tokio",
|
||||
"tokio-stream",
|
||||
"tokio-util",
|
||||
"tower-service",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-graphql-derive"
|
||||
version = "7.0.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac38b4dd452d529d6c0248b51df23603f0a875770352e26ae8c346ce6c149b3e"
|
||||
dependencies = [
|
||||
"Inflector",
|
||||
"async-graphql-parser",
|
||||
"darling",
|
||||
"proc-macro-crate",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"strum",
|
||||
"syn 2.0.92",
|
||||
"thiserror 1.0.69",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-graphql-parser"
|
||||
version = "7.0.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42d271ddda2f55b13970928abbcbc3423cfc18187c60e8769b48f21a93b7adaa"
|
||||
dependencies = [
|
||||
"async-graphql-value",
|
||||
"pest",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-graphql-value"
|
||||
version = "7.0.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aefe909173a037eaf3281b046dc22580b59a38b765d7b8d5116f2ffef098048d"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"indexmap 2.7.0",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-stream"
|
||||
version = "0.3.6"
|
||||
@ -282,6 +390,7 @@ dependencies = [
|
||||
"async-trait",
|
||||
"axum-core",
|
||||
"axum-macros",
|
||||
"base64 0.22.1",
|
||||
"bytes",
|
||||
"futures-util",
|
||||
"http 1.2.0",
|
||||
@ -300,8 +409,10 @@ dependencies = [
|
||||
"serde_json",
|
||||
"serde_path_to_error",
|
||||
"serde_urlencoded",
|
||||
"sha1",
|
||||
"sync_wrapper",
|
||||
"tokio",
|
||||
"tokio-tungstenite",
|
||||
"tower 0.5.2",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
@ -722,6 +833,9 @@ name = "bytes"
|
||||
version = "1.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bytesize"
|
||||
@ -1576,6 +1690,15 @@ dependencies = [
|
||||
"regex-syntax 0.8.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fast_chemail"
|
||||
version = "0.9.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "495a39d30d624c2caabe6312bfead73e7717692b44e0b32df168c275a2e8e9e4"
|
||||
dependencies = [
|
||||
"ascii_utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "2.3.0"
|
||||
@ -1768,6 +1891,12 @@ version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988"
|
||||
|
||||
[[package]]
|
||||
name = "futures-timer"
|
||||
version = "3.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24"
|
||||
|
||||
[[package]]
|
||||
name = "futures-util"
|
||||
version = "0.3.31"
|
||||
@ -1904,6 +2033,20 @@ dependencies = [
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "handlebars"
|
||||
version = "5.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d08485b96a0e6393e9e4d1b8d48cf74ad6c063cd905eb33f42c1ce3f0377539b"
|
||||
dependencies = [
|
||||
"log",
|
||||
"pest",
|
||||
"pest_derive",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror 1.0.69",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.12.3"
|
||||
@ -4369,6 +4512,8 @@ name = "recorder"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-graphql",
|
||||
"async-graphql-axum",
|
||||
"async-trait",
|
||||
"axum",
|
||||
"axum-auth",
|
||||
@ -5874,6 +6019,12 @@ version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||
|
||||
[[package]]
|
||||
name = "static_assertions_next"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d7beae5182595e9a8b683fa98c4317f956c9a2dec3b9716990d20023cc60c766"
|
||||
|
||||
[[package]]
|
||||
name = "string_cache"
|
||||
version = "0.8.7"
|
||||
@ -5945,6 +6096,22 @@ name = "strum"
|
||||
version = "0.26.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06"
|
||||
dependencies = [
|
||||
"strum_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strum_macros"
|
||||
version = "0.26.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be"
|
||||
dependencies = [
|
||||
"heck 0.5.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustversion",
|
||||
"syn 2.0.92",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "subtle"
|
||||
@ -6328,6 +6495,18 @@ dependencies = [
|
||||
"xattr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-tungstenite"
|
||||
version = "0.24.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "edc5f74e248dc973e0dbb7b74c7e0d6fcc301c694ff50049504004ef4d0cdcd9"
|
||||
dependencies = [
|
||||
"futures-util",
|
||||
"log",
|
||||
"tokio",
|
||||
"tungstenite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-util"
|
||||
version = "0.7.13"
|
||||
@ -6336,6 +6515,7 @@ checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-core",
|
||||
"futures-io",
|
||||
"futures-sink",
|
||||
"pin-project-lite",
|
||||
"tokio",
|
||||
@ -6559,6 +6739,24 @@ version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
|
||||
|
||||
[[package]]
|
||||
name = "tungstenite"
|
||||
version = "0.24.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "18e5b8366ee7a95b16d32197d0b2604b43a0be89dc5fac9f8e96ccafbaedda8a"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"bytes",
|
||||
"data-encoding",
|
||||
"http 1.2.0",
|
||||
"httparse",
|
||||
"log",
|
||||
"rand",
|
||||
"sha1",
|
||||
"thiserror 1.0.69",
|
||||
"utf-8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typed-builder"
|
||||
version = "0.20.0"
|
||||
|
@ -44,7 +44,7 @@ axum = "0.7.9"
|
||||
uuid = { version = "1.6.0", features = ["v4"] }
|
||||
tracing-subscriber = { version = "0.3", features = ["env-filter", "json"] }
|
||||
sea-orm-migration = { version = "1", features = ["runtime-tokio-rustls"] }
|
||||
reqwest = "0.12.9"
|
||||
reqwest = { version = "0.12.9" }
|
||||
thiserror = "2"
|
||||
rss = "2"
|
||||
bytes = "1.9"
|
||||
@ -85,6 +85,8 @@ testcontainers-modules = { version = "0.11.4", optional = true }
|
||||
log = "0.4.22"
|
||||
anyhow = "1.0.95"
|
||||
bollard = { version = "0.18", optional = true }
|
||||
async-graphql = "7.0.13"
|
||||
async-graphql-axum = "7.0.13"
|
||||
|
||||
|
||||
[dev-dependencies]
|
||||
|
@ -20,7 +20,7 @@ use crate::{
|
||||
dal::{AppDalClient, AppDalInitalizer},
|
||||
extract::mikan::{client::AppMikanClientInitializer, AppMikanClient},
|
||||
migrations::Migrator,
|
||||
models::entities::subscribers,
|
||||
models::subscribers,
|
||||
workers::subscription_worker::SubscriptionWorker,
|
||||
};
|
||||
|
||||
|
@ -24,7 +24,7 @@ pub struct OidcAuthConfig {
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(tag = "type", rename_all = "snake_case")]
|
||||
#[serde(tag = "auth_type", rename_all = "snake_case")]
|
||||
pub enum AppAuthConfig {
|
||||
Basic(BasicAuthConfig),
|
||||
Oidc(OidcAuthConfig),
|
||||
|
@ -1,6 +1,6 @@
|
||||
use loco_rs::prelude::*;
|
||||
|
||||
use crate::{models::entities::subscribers, views::subscribers::CurrentResponse};
|
||||
use crate::{models::subscribers, views::subscribers::CurrentResponse};
|
||||
|
||||
async fn current(State(ctx): State<AppContext>) -> Result<impl IntoResponse> {
|
||||
let subscriber = subscribers::Model::find_root(&ctx).await?;
|
||||
|
@ -38,7 +38,6 @@ pub enum Bangumi {
|
||||
Id,
|
||||
MikanBangumiId,
|
||||
DisplayName,
|
||||
SubscriptionId,
|
||||
SubscriberId,
|
||||
RawName,
|
||||
Season,
|
||||
@ -54,6 +53,14 @@ pub enum Bangumi {
|
||||
Extra,
|
||||
}
|
||||
|
||||
#[derive(DeriveIden)]
|
||||
pub enum SubscriptionBangumi {
|
||||
Table,
|
||||
Id,
|
||||
SubscriptionId,
|
||||
BangumiId,
|
||||
}
|
||||
|
||||
#[derive(DeriveIden)]
|
||||
pub enum Episodes {
|
||||
Table,
|
||||
@ -62,7 +69,6 @@ pub enum Episodes {
|
||||
RawName,
|
||||
DisplayName,
|
||||
BangumiId,
|
||||
SubscriptionId,
|
||||
SubscriberId,
|
||||
DownloadId,
|
||||
SavePath,
|
||||
@ -79,13 +85,23 @@ pub enum Episodes {
|
||||
Extra,
|
||||
}
|
||||
|
||||
#[derive(DeriveIden)]
|
||||
pub enum SubscriptionEpisode {
|
||||
Table,
|
||||
Id,
|
||||
SubscriptionId,
|
||||
EpisodeId,
|
||||
}
|
||||
|
||||
#[derive(DeriveIden)]
|
||||
pub enum Downloads {
|
||||
Table,
|
||||
Id,
|
||||
OriginalName,
|
||||
RawName,
|
||||
DisplayName,
|
||||
SubscriptionId,
|
||||
SubscriberId,
|
||||
DownloaderId,
|
||||
EpisodeId,
|
||||
Status,
|
||||
CurrSize,
|
||||
AllSize,
|
||||
|
@ -2,7 +2,8 @@ use loco_rs::schema::jsonb_null;
|
||||
use sea_orm_migration::{prelude::*, schema::*};
|
||||
|
||||
use super::defs::{
|
||||
Bangumi, CustomSchemaManagerExt, Episodes, GeneralIds, Subscribers, Subscriptions,
|
||||
Bangumi, CustomSchemaManagerExt, Episodes, GeneralIds, Subscribers, SubscriptionBangumi,
|
||||
SubscriptionEpisode, Subscriptions,
|
||||
};
|
||||
use crate::models::{
|
||||
subscribers::SEED_SUBSCRIBER,
|
||||
@ -37,12 +38,15 @@ impl MigrationTrait for Migration {
|
||||
)
|
||||
.await?;
|
||||
|
||||
let insert = Query::insert()
|
||||
manager
|
||||
.exec_stmt(
|
||||
Query::insert()
|
||||
.into_table(Subscribers::Table)
|
||||
.columns([Subscribers::Pid, Subscribers::DisplayName])
|
||||
.values_panic([SEED_SUBSCRIBER.into(), SEED_SUBSCRIBER.into()])
|
||||
.to_owned();
|
||||
manager.exec_stmt(insert).await?;
|
||||
.to_owned(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
create_postgres_enum_for_active_enum!(
|
||||
manager,
|
||||
@ -70,7 +74,7 @@ impl MigrationTrait for Migration {
|
||||
.name("fk_subscriptions_subscriber_id")
|
||||
.from(Subscriptions::Table, Subscriptions::SubscriberId)
|
||||
.to(Subscribers::Table, Subscribers::Id)
|
||||
.on_update(ForeignKeyAction::Restrict)
|
||||
.on_update(ForeignKeyAction::Cascade)
|
||||
.on_delete(ForeignKeyAction::Cascade),
|
||||
)
|
||||
.to_owned(),
|
||||
@ -89,7 +93,6 @@ impl MigrationTrait for Migration {
|
||||
table_auto(Bangumi::Table)
|
||||
.col(pk_auto(Bangumi::Id))
|
||||
.col(text_null(Bangumi::MikanBangumiId))
|
||||
.col(integer(Bangumi::SubscriptionId))
|
||||
.col(integer(Bangumi::SubscriberId))
|
||||
.col(text(Bangumi::DisplayName))
|
||||
.col(text(Bangumi::RawName))
|
||||
@ -104,22 +107,24 @@ impl MigrationTrait for Migration {
|
||||
.col(boolean(Bangumi::Deleted).default(false))
|
||||
.col(text_null(Bangumi::Homepage))
|
||||
.col(jsonb_null(Bangumi::Extra))
|
||||
.foreign_key(
|
||||
ForeignKey::create()
|
||||
.name("fk_bangumi_subscription_id")
|
||||
.from(Bangumi::Table, Bangumi::SubscriptionId)
|
||||
.to(Subscriptions::Table, Subscriptions::Id)
|
||||
.on_update(ForeignKeyAction::Restrict)
|
||||
.on_delete(ForeignKeyAction::Cascade),
|
||||
)
|
||||
.foreign_key(
|
||||
ForeignKey::create()
|
||||
.name("fk_bangumi_subscriber_id")
|
||||
.from(Bangumi::Table, Bangumi::SubscriberId)
|
||||
.to(Subscribers::Table, Subscribers::Id)
|
||||
.on_update(ForeignKeyAction::Restrict)
|
||||
.on_update(ForeignKeyAction::Cascade)
|
||||
.on_delete(ForeignKeyAction::Cascade),
|
||||
)
|
||||
.index(
|
||||
Index::create()
|
||||
.if_not_exists()
|
||||
.name("idx_bangumi_mikan_bangumi_id_mikan_fansub_id_subscriber_id")
|
||||
.table(Bangumi::Table)
|
||||
.col(Bangumi::MikanBangumiId)
|
||||
.col(Bangumi::MikanFansubId)
|
||||
.col(Bangumi::SubscriberId)
|
||||
.unique(),
|
||||
)
|
||||
.to_owned(),
|
||||
)
|
||||
.await?;
|
||||
@ -150,6 +155,44 @@ impl MigrationTrait for Migration {
|
||||
.create_postgres_auto_update_ts_trigger_for_col(Bangumi::Table, GeneralIds::UpdatedAt)
|
||||
.await?;
|
||||
|
||||
manager
|
||||
.create_table(
|
||||
table_auto(SubscriptionBangumi::Table)
|
||||
.col(pk_auto(SubscriptionBangumi::Id))
|
||||
.col(integer(SubscriptionBangumi::SubscriptionId))
|
||||
.col(integer(SubscriptionBangumi::BangumiId))
|
||||
.foreign_key(
|
||||
ForeignKey::create()
|
||||
.name("fk_subscription_bangumi_subscription_id")
|
||||
.from(
|
||||
SubscriptionBangumi::Table,
|
||||
SubscriptionBangumi::SubscriptionId,
|
||||
)
|
||||
.to(Subscriptions::Table, Subscriptions::Id)
|
||||
.on_update(ForeignKeyAction::Cascade)
|
||||
.on_delete(ForeignKeyAction::Cascade),
|
||||
)
|
||||
.foreign_key(
|
||||
ForeignKey::create()
|
||||
.name("fk_subscription_bangumi_bangumi_id")
|
||||
.from(SubscriptionBangumi::Table, SubscriptionBangumi::BangumiId)
|
||||
.to(Bangumi::Table, Bangumi::Id)
|
||||
.on_update(ForeignKeyAction::Cascade)
|
||||
.on_delete(ForeignKeyAction::Cascade),
|
||||
)
|
||||
.index(
|
||||
Index::create()
|
||||
.if_not_exists()
|
||||
.name("constraint_subscription_bangumi_subscription_id_bangumi_id")
|
||||
.table(SubscriptionBangumi::Table)
|
||||
.col(SubscriptionBangumi::SubscriptionId)
|
||||
.col(SubscriptionBangumi::BangumiId)
|
||||
.unique(),
|
||||
)
|
||||
.to_owned(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
manager
|
||||
.create_table(
|
||||
table_auto(Episodes::Table)
|
||||
@ -158,7 +201,6 @@ impl MigrationTrait for Migration {
|
||||
.col(text(Episodes::RawName))
|
||||
.col(text(Episodes::DisplayName))
|
||||
.col(integer(Episodes::BangumiId))
|
||||
.col(integer(Episodes::SubscriptionId))
|
||||
.col(integer(Episodes::SubscriberId))
|
||||
.col(text_null(Episodes::SavePath))
|
||||
.col(text_null(Episodes::Resolution))
|
||||
@ -172,20 +214,12 @@ impl MigrationTrait for Migration {
|
||||
.col(boolean(Episodes::Deleted).default(false))
|
||||
.col(text_null(Episodes::Source))
|
||||
.col(jsonb_null(Episodes::Extra))
|
||||
.foreign_key(
|
||||
ForeignKey::create()
|
||||
.name("fk_episodes_subscription_id")
|
||||
.from(Episodes::Table, Episodes::SubscriptionId)
|
||||
.to(Subscriptions::Table, Subscriptions::Id)
|
||||
.on_update(ForeignKeyAction::Restrict)
|
||||
.on_delete(ForeignKeyAction::Cascade),
|
||||
)
|
||||
.foreign_key(
|
||||
ForeignKey::create()
|
||||
.name("fk_episodes_bangumi_id")
|
||||
.from(Episodes::Table, Episodes::BangumiId)
|
||||
.to(Bangumi::Table, Bangumi::Id)
|
||||
.on_update(ForeignKeyAction::Restrict)
|
||||
.on_update(ForeignKeyAction::Cascade)
|
||||
.on_delete(ForeignKeyAction::Cascade),
|
||||
)
|
||||
.foreign_key(
|
||||
@ -193,7 +227,7 @@ impl MigrationTrait for Migration {
|
||||
.name("fk_episodes_subscriber_id")
|
||||
.from(Episodes::Table, Episodes::SubscriberId)
|
||||
.to(Subscribers::Table, Subscribers::Id)
|
||||
.on_update(ForeignKeyAction::Restrict)
|
||||
.on_update(ForeignKeyAction::Cascade)
|
||||
.on_delete(ForeignKeyAction::Cascade),
|
||||
)
|
||||
.to_owned(),
|
||||
@ -228,12 +262,50 @@ impl MigrationTrait for Migration {
|
||||
.create_postgres_auto_update_ts_trigger_for_col(Episodes::Table, GeneralIds::UpdatedAt)
|
||||
.await?;
|
||||
|
||||
manager
|
||||
.create_table(
|
||||
table_auto(SubscriptionEpisode::Table)
|
||||
.col(pk_auto(SubscriptionEpisode::Id))
|
||||
.col(integer(SubscriptionEpisode::SubscriptionId))
|
||||
.col(integer(SubscriptionEpisode::EpisodeId))
|
||||
.foreign_key(
|
||||
ForeignKey::create()
|
||||
.name("fk_subscription_episode_subscription_id")
|
||||
.from(
|
||||
SubscriptionEpisode::Table,
|
||||
SubscriptionEpisode::SubscriptionId,
|
||||
)
|
||||
.to(Subscriptions::Table, Subscriptions::Id)
|
||||
.on_update(ForeignKeyAction::Cascade)
|
||||
.on_delete(ForeignKeyAction::Cascade),
|
||||
)
|
||||
.foreign_key(
|
||||
ForeignKey::create()
|
||||
.name("fk_subscription_episode_episode_id")
|
||||
.from(SubscriptionEpisode::Table, SubscriptionEpisode::EpisodeId)
|
||||
.to(Episodes::Table, Episodes::Id)
|
||||
.on_update(ForeignKeyAction::Cascade)
|
||||
.on_delete(ForeignKeyAction::Cascade),
|
||||
)
|
||||
.index(
|
||||
Index::create()
|
||||
.if_not_exists()
|
||||
.name("constraint_subscription_episode_subscription_id_episode_id")
|
||||
.table(SubscriptionEpisode::Table)
|
||||
.col(SubscriptionEpisode::SubscriptionId)
|
||||
.col(SubscriptionEpisode::EpisodeId)
|
||||
.unique(),
|
||||
)
|
||||
.to_owned(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||
manager
|
||||
.drop_table(Table::drop().table(Episodes::Table).to_owned())
|
||||
.drop_table(Table::drop().table(SubscriptionEpisode::Table).to_owned())
|
||||
.await?;
|
||||
|
||||
manager
|
||||
@ -241,7 +313,11 @@ impl MigrationTrait for Migration {
|
||||
.await?;
|
||||
|
||||
manager
|
||||
.drop_table(Table::drop().table(Bangumi::Table).to_owned())
|
||||
.drop_table(Table::drop().table(Episodes::Table).to_owned())
|
||||
.await?;
|
||||
|
||||
manager
|
||||
.drop_table(Table::drop().table(SubscriptionBangumi::Table).to_owned())
|
||||
.await?;
|
||||
|
||||
manager
|
||||
@ -249,7 +325,7 @@ impl MigrationTrait for Migration {
|
||||
.await?;
|
||||
|
||||
manager
|
||||
.drop_table(Table::drop().table(Subscriptions::Table).to_owned())
|
||||
.drop_table(Table::drop().table(Bangumi::Table).to_owned())
|
||||
.await?;
|
||||
|
||||
manager
|
||||
@ -260,7 +336,7 @@ impl MigrationTrait for Migration {
|
||||
.await?;
|
||||
|
||||
manager
|
||||
.drop_table(Table::drop().table(Subscribers::Table).to_owned())
|
||||
.drop_table(Table::drop().table(Subscriptions::Table).to_owned())
|
||||
.await?;
|
||||
|
||||
manager
|
||||
@ -268,17 +344,21 @@ impl MigrationTrait for Migration {
|
||||
.await?;
|
||||
|
||||
manager
|
||||
.drop_postgres_enum_for_active_enum(subscriptions::SubscriptionCategoryEnum)
|
||||
.drop_table(Table::drop().table(Subscribers::Table).to_owned())
|
||||
.await?;
|
||||
|
||||
manager
|
||||
.drop_postgres_auto_update_ts_fn_for_col(GeneralIds::UpdatedAt)
|
||||
.drop_postgres_enum_for_active_enum(subscriptions::SubscriptionCategoryEnum)
|
||||
.await?;
|
||||
|
||||
manager
|
||||
.drop_postgres_enum_for_active_enum(SubscriptionCategoryEnum)
|
||||
.await?;
|
||||
|
||||
manager
|
||||
.drop_postgres_auto_update_ts_fn_for_col(GeneralIds::UpdatedAt)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -2,9 +2,12 @@ use loco_rs::schema::table_auto;
|
||||
use sea_orm_migration::{prelude::*, schema::*};
|
||||
|
||||
use super::defs::*;
|
||||
use crate::models::prelude::{
|
||||
use crate::models::{
|
||||
downloaders::DownloaderCategoryEnum,
|
||||
prelude::{
|
||||
downloads::{DownloadMimeEnum, DownloadStatusEnum},
|
||||
DownloadMime, DownloadStatus,
|
||||
DownloadMime, DownloadStatus, DownloaderCategory,
|
||||
},
|
||||
};
|
||||
|
||||
#[derive(DeriveMigrationName)]
|
||||
@ -13,6 +16,48 @@ pub struct Migration;
|
||||
#[async_trait::async_trait]
|
||||
impl MigrationTrait for Migration {
|
||||
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||
create_postgres_enum_for_active_enum!(
|
||||
manager,
|
||||
DownloaderCategoryEnum,
|
||||
DownloaderCategory::QBittorrent
|
||||
)
|
||||
.await?;
|
||||
|
||||
manager
|
||||
.create_table(
|
||||
table_auto(Downloaders::Table)
|
||||
.col(pk_auto(Downloaders::Id))
|
||||
.col(text(Downloaders::Endpoint))
|
||||
.col(string_null(Downloaders::Username))
|
||||
.col(string_null(Downloaders::Password))
|
||||
.col(enumeration(
|
||||
Downloaders::Category,
|
||||
DownloaderCategoryEnum,
|
||||
DownloaderCategory::iden_values(),
|
||||
))
|
||||
.col(text(Downloaders::SavePath))
|
||||
.col(integer(Downloaders::SubscriberId))
|
||||
.foreign_key(
|
||||
ForeignKey::create()
|
||||
.name("fk_downloader_subscriber_id")
|
||||
.from_tbl(Downloaders::Table)
|
||||
.from_col(Downloaders::SubscriberId)
|
||||
.to_tbl(Subscribers::Table)
|
||||
.to_col(Subscribers::Id)
|
||||
.on_delete(ForeignKeyAction::Cascade)
|
||||
.on_update(ForeignKeyAction::Cascade),
|
||||
)
|
||||
.to_owned(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
manager
|
||||
.create_postgres_auto_update_ts_trigger_for_col(
|
||||
Downloaders::Table,
|
||||
GeneralIds::UpdatedAt,
|
||||
)
|
||||
.await?;
|
||||
|
||||
create_postgres_enum_for_active_enum!(
|
||||
manager,
|
||||
DownloadMimeEnum,
|
||||
@ -37,9 +82,11 @@ impl MigrationTrait for Migration {
|
||||
.create_table(
|
||||
table_auto(Downloads::Table)
|
||||
.col(pk_auto(Downloads::Id))
|
||||
.col(string(Downloads::OriginalName))
|
||||
.col(string(Downloads::RawName))
|
||||
.col(string(Downloads::DisplayName))
|
||||
.col(integer(Downloads::SubscriptionId))
|
||||
.col(integer(Downloads::SubscriberId))
|
||||
.col(integer(Downloads::DownloaderId))
|
||||
.col(integer(Downloads::EpisodeId))
|
||||
.col(enumeration(
|
||||
Downloads::Status,
|
||||
DownloadStatusEnum,
|
||||
@ -57,16 +104,42 @@ impl MigrationTrait for Migration {
|
||||
.col(text_null(Downloads::SavePath))
|
||||
.foreign_key(
|
||||
ForeignKey::create()
|
||||
.name("fk_downloads_subscription_id")
|
||||
.from(Downloads::Table, Downloads::SubscriptionId)
|
||||
.to(Subscriptions::Table, Subscriptions::Id)
|
||||
.on_update(ForeignKeyAction::Restrict)
|
||||
.on_delete(ForeignKeyAction::Cascade),
|
||||
.name("fk_downloads_subscriber_id")
|
||||
.from_tbl(Downloads::Table)
|
||||
.from_col(Downloads::SubscriberId)
|
||||
.to_tbl(Subscribers::Table)
|
||||
.to_col(Subscribers::Id)
|
||||
.on_delete(ForeignKeyAction::Cascade)
|
||||
.on_update(ForeignKeyAction::Cascade),
|
||||
)
|
||||
.foreign_key(
|
||||
ForeignKey::create()
|
||||
.name("fk_downloads_downloader_id")
|
||||
.from_tbl(Downloads::Table)
|
||||
.from_col(Downloads::DownloaderId)
|
||||
.to_tbl(Downloaders::Table)
|
||||
.to_col(Downloaders::Id)
|
||||
.on_delete(ForeignKeyAction::Cascade)
|
||||
.on_update(ForeignKeyAction::Cascade),
|
||||
)
|
||||
.foreign_key(
|
||||
ForeignKey::create()
|
||||
.name("fk_downloads_episode_id")
|
||||
.from_tbl(Downloads::Table)
|
||||
.from_col(Downloads::EpisodeId)
|
||||
.to_tbl(Episodes::Table)
|
||||
.to_col(Episodes::Id)
|
||||
.on_delete(ForeignKeyAction::Cascade)
|
||||
.on_update(ForeignKeyAction::Cascade),
|
||||
)
|
||||
.to_owned(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
manager
|
||||
.create_postgres_auto_update_ts_trigger_for_col(Downloads::Table, GeneralIds::UpdatedAt)
|
||||
.await?;
|
||||
|
||||
manager
|
||||
.create_index(
|
||||
Index::create()
|
||||
@ -78,37 +151,12 @@ impl MigrationTrait for Migration {
|
||||
)
|
||||
.await?;
|
||||
|
||||
manager
|
||||
.alter_table(
|
||||
Table::alter()
|
||||
.table(Episodes::Table)
|
||||
.add_column_if_not_exists(integer_null(Episodes::DownloadId))
|
||||
.add_foreign_key(
|
||||
TableForeignKey::new()
|
||||
.name("fk_episodes_download_id")
|
||||
.from_tbl(Episodes::Table)
|
||||
.from_col(Episodes::DownloadId)
|
||||
.to_tbl(Downloads::Table)
|
||||
.to_col(Downloads::Id)
|
||||
.on_update(ForeignKeyAction::Restrict)
|
||||
.on_delete(ForeignKeyAction::SetNull),
|
||||
)
|
||||
.to_owned(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||
manager
|
||||
.alter_table(
|
||||
Table::alter()
|
||||
.table(Episodes::Table)
|
||||
.drop_foreign_key(Alias::new("fk_episodes_download_id"))
|
||||
.drop_column(Episodes::DownloadId)
|
||||
.to_owned(),
|
||||
)
|
||||
.drop_postgres_auto_update_ts_trigger_for_col(Downloads::Table, GeneralIds::UpdatedAt)
|
||||
.await?;
|
||||
|
||||
manager
|
||||
@ -123,6 +171,18 @@ impl MigrationTrait for Migration {
|
||||
.drop_postgres_enum_for_active_enum(DownloadStatusEnum)
|
||||
.await?;
|
||||
|
||||
manager
|
||||
.drop_postgres_auto_update_ts_trigger_for_col(Downloaders::Table, GeneralIds::UpdatedAt)
|
||||
.await?;
|
||||
|
||||
manager
|
||||
.drop_table(Table::drop().table(Downloaders::Table).to_owned())
|
||||
.await?;
|
||||
|
||||
manager
|
||||
.drop_postgres_enum_for_active_enum(DownloaderCategoryEnum)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,10 @@ use sea_orm_migration::{prelude::*, schema::*};
|
||||
use super::defs::Auth;
|
||||
use crate::{
|
||||
migrations::defs::{CustomSchemaManagerExt, GeneralIds, Subscribers},
|
||||
models::auth::{AuthType, AuthTypeEnum},
|
||||
models::{
|
||||
auth::{AuthType, AuthTypeEnum},
|
||||
subscribers::SEED_SUBSCRIBER,
|
||||
},
|
||||
};
|
||||
|
||||
#[derive(DeriveMigrationName)]
|
||||
@ -40,7 +43,7 @@ impl MigrationTrait for Migration {
|
||||
.to_tbl(Subscribers::Table)
|
||||
.to_col(Subscribers::Id)
|
||||
.on_delete(ForeignKeyAction::Cascade)
|
||||
.on_update(ForeignKeyAction::Restrict),
|
||||
.on_update(ForeignKeyAction::Cascade),
|
||||
)
|
||||
.to_owned(),
|
||||
)
|
||||
@ -62,6 +65,20 @@ impl MigrationTrait for Migration {
|
||||
.create_postgres_auto_update_ts_trigger_for_col(Auth::Table, GeneralIds::UpdatedAt)
|
||||
.await?;
|
||||
|
||||
manager
|
||||
.exec_stmt(
|
||||
Query::insert()
|
||||
.into_table(Auth::Table)
|
||||
.columns([Auth::Pid, Auth::AuthType, Auth::SubscriberId])
|
||||
.values_panic([
|
||||
SEED_SUBSCRIBER.into(),
|
||||
SimpleExpr::from(AuthType::Basic).as_enum(AuthTypeEnum),
|
||||
1.into(),
|
||||
])
|
||||
.to_owned(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -4,7 +4,6 @@ pub use sea_orm_migration::prelude::*;
|
||||
pub mod defs;
|
||||
pub mod m20220101_000001_init;
|
||||
pub mod m20240224_082543_add_downloads;
|
||||
pub mod m20240225_060853_subscriber_add_downloader;
|
||||
pub mod m20241231_000001_auth;
|
||||
|
||||
pub struct Migrator;
|
||||
@ -15,7 +14,6 @@ impl MigratorTrait for Migrator {
|
||||
vec![
|
||||
Box::new(m20220101_000001_init::Migration),
|
||||
Box::new(m20240224_082543_add_downloads::Migration),
|
||||
Box::new(m20240225_060853_subscriber_add_downloader::Migration),
|
||||
Box::new(m20241231_000001_auth::Migration),
|
||||
]
|
||||
}
|
||||
|
@ -1,6 +1,54 @@
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
pub use super::entities::auth::*;
|
||||
#[derive(
|
||||
Clone, Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum, DeriveDisplay, Serialize, Deserialize,
|
||||
)]
|
||||
#[sea_orm(rs_type = "String", db_type = "Enum", enum_name = "auth_type")]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum AuthType {
|
||||
#[sea_orm(string_value = "basic")]
|
||||
Basic,
|
||||
#[sea_orm(string_value = "oidc")]
|
||||
Oidc,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, DeriveEntityModel)]
|
||||
#[sea_orm(table_name = "auth")]
|
||||
pub struct Model {
|
||||
pub created_at: DateTime,
|
||||
pub updated_at: DateTime,
|
||||
#[sea_orm(primary_key)]
|
||||
pub id: i32,
|
||||
pub pid: String,
|
||||
pub subscriber_id: i32,
|
||||
pub auth_type: AuthType,
|
||||
pub avatar_url: 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"
|
||||
)]
|
||||
SubscriberId,
|
||||
}
|
||||
|
||||
impl Related<super::subscribers::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::SubscriberId.def()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelatedEntity)]
|
||||
pub enum RelatedEntity {
|
||||
#[sea_orm(entity = "super::subscribers::Entity")]
|
||||
Subscriber,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
||||
|
@ -1,7 +1,87 @@
|
||||
use loco_rs::app::AppContext;
|
||||
use sea_orm::{entity::prelude::*, ActiveValue, TryIntoModel};
|
||||
use sea_orm::{entity::prelude::*, sea_query::OnConflict, ActiveValue, FromJsonQueryResult};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
pub use super::entities::bangumi::*;
|
||||
use super::subscription_bangumi;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, FromJsonQueryResult)]
|
||||
pub struct BangumiFilter {
|
||||
pub name: Option<Vec<String>>,
|
||||
pub group: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, FromJsonQueryResult)]
|
||||
pub struct BangumiExtra {
|
||||
pub name_zh: Option<String>,
|
||||
pub s_name_zh: Option<String>,
|
||||
pub name_en: Option<String>,
|
||||
pub s_name_en: Option<String>,
|
||||
pub name_jp: Option<String>,
|
||||
pub s_name_jp: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
|
||||
#[sea_orm(table_name = "bangumi")]
|
||||
pub struct Model {
|
||||
pub created_at: DateTime,
|
||||
pub updated_at: DateTime,
|
||||
#[sea_orm(primary_key)]
|
||||
pub id: i32,
|
||||
pub mikan_bangumi_id: Option<String>,
|
||||
pub subscriber_id: i32,
|
||||
pub display_name: String,
|
||||
pub raw_name: String,
|
||||
pub season: i32,
|
||||
pub season_raw: Option<String>,
|
||||
pub fansub: Option<String>,
|
||||
pub mikan_fansub_id: Option<String>,
|
||||
pub filter: Option<BangumiFilter>,
|
||||
pub rss_link: Option<String>,
|
||||
pub poster_link: Option<String>,
|
||||
pub save_path: Option<String>,
|
||||
#[sea_orm(default = "false")]
|
||||
pub deleted: bool,
|
||||
pub homepage: Option<String>,
|
||||
pub extra: Option<BangumiExtra>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
pub enum Relation {
|
||||
#[sea_orm(has_many = "super::subscriptions::Entity")]
|
||||
Subscription,
|
||||
#[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::episodes::Entity")]
|
||||
Episode,
|
||||
}
|
||||
|
||||
impl Related<super::episodes::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::Episode.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::subscriptions::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
super::subscription_bangumi::Relation::Subscription.def()
|
||||
}
|
||||
|
||||
fn via() -> Option<RelationDef> {
|
||||
Some(super::subscription_bangumi::Relation::Bangumi.def().rev())
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::subscribers::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::Subscriber.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl Model {
|
||||
pub async fn get_or_insert_from_mikan<F>(
|
||||
@ -30,12 +110,37 @@ impl Model {
|
||||
let mut bgm = ActiveModel {
|
||||
mikan_bangumi_id: ActiveValue::Set(Some(mikan_bangumi_id)),
|
||||
mikan_fansub_id: ActiveValue::Set(Some(mikan_fansub_id)),
|
||||
subscription_id: ActiveValue::Set(subscription_id),
|
||||
subscriber_id: ActiveValue::Set(subscriber_id),
|
||||
..Default::default()
|
||||
};
|
||||
f(&mut bgm).await?;
|
||||
let bgm: Model = bgm.save(db).await?.try_into_model()?;
|
||||
let bgm = Entity::insert(bgm)
|
||||
.on_conflict(
|
||||
OnConflict::columns([
|
||||
Column::MikanBangumiId,
|
||||
Column::MikanFansubId,
|
||||
Column::SubscriberId,
|
||||
])
|
||||
.update_columns([
|
||||
Column::RawName,
|
||||
Column::Extra,
|
||||
Column::Fansub,
|
||||
Column::PosterLink,
|
||||
Column::Season,
|
||||
Column::SeasonRaw,
|
||||
])
|
||||
.to_owned(),
|
||||
)
|
||||
.exec_with_returning(db)
|
||||
.await?;
|
||||
subscription_bangumi::Entity::insert(subscription_bangumi::ActiveModel {
|
||||
subscription_id: ActiveValue::Set(subscription_id),
|
||||
bangumi_id: ActiveValue::Set(bgm.id),
|
||||
..Default::default()
|
||||
})
|
||||
.on_conflict_do_nothing()
|
||||
.exec(db)
|
||||
.await?;
|
||||
Ok(bgm)
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,59 @@
|
||||
use sea_orm::prelude::*;
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use url::Url;
|
||||
|
||||
pub use crate::models::entities::downloaders::*;
|
||||
#[derive(
|
||||
Clone, Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum, DeriveDisplay, Serialize, Deserialize,
|
||||
)]
|
||||
#[sea_orm(rs_type = "String", db_type = "Enum", enum_name = "downloader_type")]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum DownloaderCategory {
|
||||
#[sea_orm(string_value = "qbittorrent")]
|
||||
QBittorrent,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
|
||||
#[sea_orm(table_name = "downloaders")]
|
||||
pub struct Model {
|
||||
#[sea_orm(column_type = "Timestamp")]
|
||||
pub created_at: DateTime,
|
||||
#[sea_orm(column_type = "Timestamp")]
|
||||
pub updated_at: DateTime,
|
||||
#[sea_orm(primary_key)]
|
||||
pub id: i32,
|
||||
pub category: DownloaderCategory,
|
||||
pub endpoint: String,
|
||||
pub password: String,
|
||||
pub username: String,
|
||||
pub subscriber_id: i32,
|
||||
pub save_path: 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::downloads::Entity")]
|
||||
Download,
|
||||
}
|
||||
|
||||
impl Related<super::subscribers::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::Subscriber.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::downloads::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::Download.def()
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
||||
@ -10,6 +62,7 @@ impl Model {
|
||||
pub fn get_endpoint(&self) -> String {
|
||||
self.endpoint.clone()
|
||||
}
|
||||
|
||||
pub fn endpoint_url(&self) -> Result<Url, url::ParseError> {
|
||||
let url = Url::parse(&self.endpoint)?;
|
||||
Ok(url)
|
||||
|
@ -1,27 +1,107 @@
|
||||
use sea_orm::{prelude::*, ActiveValue};
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::extract::mikan::MikanRssItem;
|
||||
pub use crate::models::entities::downloads::*;
|
||||
#[derive(
|
||||
Clone, Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum, DeriveDisplay, Serialize, Deserialize,
|
||||
)]
|
||||
#[sea_orm(rs_type = "String", db_type = "Enum", enum_name = "download_status")]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum DownloadStatus {
|
||||
#[sea_orm(string_value = "pending")]
|
||||
Pending,
|
||||
#[sea_orm(string_value = "downloading")]
|
||||
Downloading,
|
||||
#[sea_orm(string_value = "paused")]
|
||||
Paused,
|
||||
#[sea_orm(string_value = "completed")]
|
||||
Completed,
|
||||
#[sea_orm(string_value = "failed")]
|
||||
Failed,
|
||||
#[sea_orm(string_value = "deleted")]
|
||||
Deleted,
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Clone, Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum, DeriveDisplay, Serialize, Deserialize,
|
||||
)]
|
||||
#[sea_orm(rs_type = "String", db_type = "Enum", enum_name = "download_mime")]
|
||||
pub enum DownloadMime {
|
||||
#[sea_orm(string_value = "application/octet-stream")]
|
||||
#[serde(rename = "application/octet-stream")]
|
||||
OctetStream,
|
||||
#[sea_orm(string_value = "application/x-bittorrent")]
|
||||
#[serde(rename = "application/x-bittorrent")]
|
||||
BitTorrent,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
|
||||
#[sea_orm(table_name = "downloads")]
|
||||
pub struct Model {
|
||||
pub created_at: DateTime,
|
||||
pub updated_at: DateTime,
|
||||
#[sea_orm(primary_key)]
|
||||
pub id: i32,
|
||||
pub raw_name: String,
|
||||
pub display_name: String,
|
||||
pub downloader_id: i32,
|
||||
pub episode_id: i32,
|
||||
pub subscriber_id: i32,
|
||||
pub status: DownloadStatus,
|
||||
pub mime: DownloadMime,
|
||||
pub url: String,
|
||||
pub all_size: Option<u64>,
|
||||
pub curr_size: Option<u64>,
|
||||
pub homepage: Option<String>,
|
||||
pub save_path: 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(
|
||||
belongs_to = "super::downloaders::Entity",
|
||||
from = "Column::DownloaderId",
|
||||
to = "super::downloaders::Column::Id",
|
||||
on_update = "Cascade",
|
||||
on_delete = "Cascade"
|
||||
)]
|
||||
Downloader,
|
||||
#[sea_orm(
|
||||
belongs_to = "super::episodes::Entity",
|
||||
from = "Column::EpisodeId",
|
||||
to = "super::episodes::Column::Id",
|
||||
on_update = "Cascade",
|
||||
on_delete = "Cascade"
|
||||
)]
|
||||
Episode,
|
||||
}
|
||||
|
||||
impl Related<super::subscribers::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::Subscriber.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::downloaders::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::Downloader.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::episodes::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::Episode.def()
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
||||
|
||||
impl ActiveModel {
|
||||
pub fn from_mikan_rss_item(m: MikanRssItem, subscription_id: i32) -> Self {
|
||||
let _ = Self {
|
||||
origin_name: ActiveValue::Set(m.title.clone()),
|
||||
display_name: ActiveValue::Set(m.title),
|
||||
subscription_id: ActiveValue::Set(subscription_id),
|
||||
status: ActiveValue::Set(DownloadStatus::Pending),
|
||||
mime: ActiveValue::Set(DownloadMime::BitTorrent),
|
||||
url: ActiveValue::Set(m.url.to_string()),
|
||||
curr_size: ActiveValue::Set(m.content_length.as_ref().map(|_| 0)),
|
||||
all_size: ActiveValue::Set(m.content_length),
|
||||
homepage: ActiveValue::Set(Some(m.homepage.to_string())),
|
||||
..Default::default()
|
||||
};
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl Model {}
|
||||
impl ActiveModel {}
|
||||
|
@ -1,10 +1,10 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use loco_rs::app::AppContext;
|
||||
use sea_orm::{entity::prelude::*, sea_query::OnConflict, ActiveValue};
|
||||
use sea_orm::{entity::prelude::*, sea_query::OnConflict, ActiveValue, FromJsonQueryResult};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::bangumi;
|
||||
pub use super::entities::episodes::*;
|
||||
use super::{bangumi, query::InsertManyReturningExt, subscription_episode};
|
||||
use crate::{
|
||||
app::AppContextExt,
|
||||
extract::{
|
||||
@ -13,6 +13,92 @@ use crate::{
|
||||
},
|
||||
};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, FromJsonQueryResult, Default)]
|
||||
pub struct EpisodeExtra {
|
||||
pub name_zh: Option<String>,
|
||||
pub s_name_zh: Option<String>,
|
||||
pub name_en: Option<String>,
|
||||
pub s_name_en: Option<String>,
|
||||
pub name_jp: Option<String>,
|
||||
pub s_name_jp: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
|
||||
#[sea_orm(table_name = "episodes")]
|
||||
pub struct Model {
|
||||
pub created_at: DateTime,
|
||||
pub updated_at: DateTime,
|
||||
#[sea_orm(primary_key)]
|
||||
pub id: i32,
|
||||
#[sea_orm(indexed)]
|
||||
pub mikan_episode_id: Option<String>,
|
||||
pub raw_name: String,
|
||||
pub display_name: String,
|
||||
pub bangumi_id: i32,
|
||||
pub subscriber_id: i32,
|
||||
pub save_path: Option<String>,
|
||||
pub resolution: Option<String>,
|
||||
pub season: i32,
|
||||
pub season_raw: Option<String>,
|
||||
pub fansub: Option<String>,
|
||||
pub poster_link: Option<String>,
|
||||
pub episode_index: i32,
|
||||
pub homepage: Option<String>,
|
||||
pub subtitle: Option<Vec<String>>,
|
||||
#[sea_orm(default = "false")]
|
||||
pub deleted: bool,
|
||||
pub source: Option<String>,
|
||||
pub extra: EpisodeExtra,
|
||||
}
|
||||
|
||||
#[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(
|
||||
belongs_to = "super::bangumi::Entity",
|
||||
from = "Column::BangumiId",
|
||||
to = "super::bangumi::Column::Id",
|
||||
on_update = "Cascade",
|
||||
on_delete = "Cascade"
|
||||
)]
|
||||
Bangumi,
|
||||
#[sea_orm(has_many = "super::subscriptions::Entity")]
|
||||
Subscriptions,
|
||||
#[sea_orm(has_one = "super::downloads::Entity")]
|
||||
Downloads,
|
||||
}
|
||||
|
||||
impl Related<super::bangumi::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::Bangumi.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::downloads::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::Downloads.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::subscriptions::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::Subscriptions.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::subscribers::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::Subscriber.def()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct MikanEpsiodeCreation {
|
||||
pub episode: MikanEpisodeMeta,
|
||||
@ -22,6 +108,7 @@ pub struct MikanEpsiodeCreation {
|
||||
impl Model {
|
||||
pub async fn add_episodes(
|
||||
ctx: &AppContext,
|
||||
subscription_id: i32,
|
||||
creations: impl IntoIterator<Item = MikanEpsiodeCreation>,
|
||||
) -> eyre::Result<()> {
|
||||
let db = &ctx.db;
|
||||
@ -35,13 +122,33 @@ impl Model {
|
||||
})
|
||||
.flatten();
|
||||
|
||||
Entity::insert_many(new_episode_active_modes)
|
||||
let inserted_episodes = Entity::insert_many(new_episode_active_modes)
|
||||
.on_conflict(
|
||||
OnConflict::columns([Column::BangumiId, Column::MikanEpisodeId])
|
||||
.do_nothing()
|
||||
.to_owned(),
|
||||
)
|
||||
.on_empty_do_nothing()
|
||||
.exec_with_returning_columns(db, [Column::Id])
|
||||
.await?
|
||||
.into_iter()
|
||||
.flat_map(|r| r.try_get_many_by_index::<i32>());
|
||||
|
||||
let insert_subscription_episode_links = inserted_episodes.into_iter().map(|episode_id| {
|
||||
subscription_episode::ActiveModel::from_subscription_and_episode(
|
||||
subscription_id,
|
||||
episode_id,
|
||||
)
|
||||
});
|
||||
|
||||
subscription_episode::Entity::insert_many(insert_subscription_episode_links)
|
||||
.on_conflict(
|
||||
OnConflict::columns([
|
||||
subscription_episode::Column::SubscriptionId,
|
||||
subscription_episode::Column::EpisodeId,
|
||||
])
|
||||
.do_nothing()
|
||||
.to_owned(),
|
||||
)
|
||||
.exec(db)
|
||||
.await?;
|
||||
|
||||
@ -72,7 +179,6 @@ impl ActiveModel {
|
||||
raw_name: ActiveValue::Set(item.episode_title.clone()),
|
||||
display_name: ActiveValue::Set(item.episode_title.clone()),
|
||||
bangumi_id: ActiveValue::Set(bgm.id),
|
||||
subscription_id: ActiveValue::Set(bgm.subscription_id),
|
||||
subscriber_id: ActiveValue::Set(bgm.subscriber_id),
|
||||
resolution: ActiveValue::Set(raw_meta.resolution),
|
||||
season: ActiveValue::Set(if raw_meta.season > 0 {
|
||||
|
@ -2,10 +2,10 @@ pub mod auth;
|
||||
pub mod bangumi;
|
||||
pub mod downloaders;
|
||||
pub mod downloads;
|
||||
pub mod entities;
|
||||
pub mod episodes;
|
||||
pub mod notifications;
|
||||
pub mod prelude;
|
||||
pub mod query;
|
||||
pub mod subscribers;
|
||||
pub mod subscription_bangumi;
|
||||
pub mod subscription_episode;
|
||||
pub mod subscriptions;
|
||||
|
@ -1,7 +1,8 @@
|
||||
use sea_orm::{
|
||||
prelude::Expr,
|
||||
sea_query::{Alias, IntoColumnRef, IntoTableRef, Query, SelectStatement},
|
||||
Value,
|
||||
ActiveModelTrait, ColumnTrait, ConnectionTrait, DbErr, EntityTrait, Insert, IntoActiveModel,
|
||||
Iterable, QueryResult, QueryTrait, SelectModel, SelectorRaw, Value,
|
||||
};
|
||||
|
||||
pub fn filter_values_in<
|
||||
@ -24,3 +25,76 @@ pub fn filter_values_in<
|
||||
.and_where(Expr::col(col_ref).is_not_null())
|
||||
.to_owned()
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
pub trait InsertManyReturningExt<A>: Sized
|
||||
where
|
||||
<A::Entity as EntityTrait>::Model: IntoActiveModel<A>,
|
||||
A: ActiveModelTrait,
|
||||
{
|
||||
fn exec_with_returning_models<C>(
|
||||
self,
|
||||
db: &C,
|
||||
) -> SelectorRaw<SelectModel<<A::Entity as EntityTrait>::Model>>
|
||||
where
|
||||
C: ConnectionTrait;
|
||||
|
||||
async fn exec_with_returning_columns<C, I>(
|
||||
self,
|
||||
db: &C,
|
||||
columns: I,
|
||||
) -> Result<Vec<QueryResult>, DbErr>
|
||||
where
|
||||
C: ConnectionTrait,
|
||||
I: IntoIterator<Item = <A::Entity as EntityTrait>::Column> + Send;
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl<A> InsertManyReturningExt<A> for Insert<A>
|
||||
where
|
||||
<A::Entity as EntityTrait>::Model: IntoActiveModel<A>,
|
||||
A: ActiveModelTrait + Send,
|
||||
{
|
||||
fn exec_with_returning_models<C>(
|
||||
self,
|
||||
db: &C,
|
||||
) -> SelectorRaw<SelectModel<<A::Entity as EntityTrait>::Model>>
|
||||
where
|
||||
C: ConnectionTrait,
|
||||
{
|
||||
let mut insert_statement = self.into_query();
|
||||
let db_backend = db.get_database_backend();
|
||||
let returning = Query::returning().exprs(
|
||||
<A::Entity as EntityTrait>::Column::iter()
|
||||
.map(|c| c.select_as(c.into_returning_expr(db_backend))),
|
||||
);
|
||||
insert_statement.returning(returning);
|
||||
let insert_statement = db_backend.build(&insert_statement);
|
||||
SelectorRaw::<SelectModel<<A::Entity as EntityTrait>::Model>>::from_statement(
|
||||
insert_statement,
|
||||
)
|
||||
}
|
||||
|
||||
async fn exec_with_returning_columns<C, I>(
|
||||
self,
|
||||
db: &C,
|
||||
columns: I,
|
||||
) -> Result<Vec<QueryResult>, DbErr>
|
||||
where
|
||||
C: ConnectionTrait,
|
||||
I: IntoIterator<Item = <A::Entity as EntityTrait>::Column> + Send,
|
||||
{
|
||||
let mut insert_statement = self.into_query();
|
||||
let db_backend = db.get_database_backend();
|
||||
let returning = Query::returning().exprs(
|
||||
columns
|
||||
.into_iter()
|
||||
.map(|c| c.select_as(c.into_returning_expr(db_backend))),
|
||||
);
|
||||
insert_statement.returning(returning);
|
||||
|
||||
let statement = db_backend.build(&insert_statement);
|
||||
|
||||
db.query_all(statement).await
|
||||
}
|
||||
}
|
||||
|
@ -2,13 +2,73 @@ use loco_rs::{
|
||||
app::AppContext,
|
||||
model::{ModelError, ModelResult},
|
||||
};
|
||||
use sea_orm::{entity::prelude::*, ActiveValue, TransactionTrait};
|
||||
use sea_orm::{entity::prelude::*, ActiveValue, FromJsonQueryResult, TransactionTrait};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
pub use super::entities::subscribers::*;
|
||||
|
||||
pub const SEED_SUBSCRIBER: &str = "konobangu";
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, FromJsonQueryResult)]
|
||||
pub struct SubscriberBangumiConfig {
|
||||
pub leading_group_tag: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
|
||||
#[sea_orm(table_name = "subscribers")]
|
||||
pub struct Model {
|
||||
pub created_at: DateTime,
|
||||
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>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
pub enum Relation {
|
||||
#[sea_orm(has_many = "super::subscriptions::Entity")]
|
||||
Subscription,
|
||||
#[sea_orm(has_many = "super::downloaders::Entity")]
|
||||
Downloader,
|
||||
#[sea_orm(has_many = "super::bangumi::Entity")]
|
||||
Bangumi,
|
||||
#[sea_orm(has_many = "super::episodes::Entity")]
|
||||
Episode,
|
||||
#[sea_orm(has_many = "super::auth::Entity")]
|
||||
Auth,
|
||||
}
|
||||
|
||||
impl Related<super::subscriptions::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::Subscription.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::downloaders::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::Downloader.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::bangumi::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::Bangumi.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::episodes::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::Episode.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::auth::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::Auth.def()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct SubscriberIdParams {
|
||||
pub id: String,
|
||||
|
56
apps/recorder/src/models/subscription_bangumi.rs
Normal file
56
apps/recorder/src/models/subscription_bangumi.rs
Normal file
@ -0,0 +1,56 @@
|
||||
use sea_orm::{entity::prelude::*, ActiveValue};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
|
||||
#[sea_orm(table_name = "subscription_bangumi")]
|
||||
pub struct Model {
|
||||
#[sea_orm(primary_key)]
|
||||
pub id: i32,
|
||||
pub subscription_id: i32,
|
||||
pub bangumi_id: i32,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
pub enum Relation {
|
||||
#[sea_orm(
|
||||
belongs_to = "super::subscriptions::Entity",
|
||||
from = "Column::SubscriptionId",
|
||||
to = "super::subscriptions::Column::Id",
|
||||
on_update = "Cascade",
|
||||
on_delete = "Cascade"
|
||||
)]
|
||||
Subscription,
|
||||
#[sea_orm(
|
||||
belongs_to = "super::bangumi::Entity",
|
||||
from = "Column::BangumiId",
|
||||
to = "super::bangumi::Column::Id",
|
||||
on_update = "Cascade",
|
||||
on_delete = "Cascade"
|
||||
)]
|
||||
Bangumi,
|
||||
}
|
||||
|
||||
impl Related<super::subscriptions::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::Subscription.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::bangumi::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::Bangumi.def()
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
||||
|
||||
impl ActiveModel {
|
||||
pub fn from_subscription_and_bangumi(subscription_id: i32, bangumi_id: i32) -> Self {
|
||||
Self {
|
||||
subscription_id: ActiveValue::Set(subscription_id),
|
||||
bangumi_id: ActiveValue::Set(bangumi_id),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
56
apps/recorder/src/models/subscription_episode.rs
Normal file
56
apps/recorder/src/models/subscription_episode.rs
Normal file
@ -0,0 +1,56 @@
|
||||
use sea_orm::{entity::prelude::*, ActiveValue};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
|
||||
#[sea_orm(table_name = "subscription_episode")]
|
||||
pub struct Model {
|
||||
#[sea_orm(primary_key)]
|
||||
pub id: i32,
|
||||
pub subscription_id: i32,
|
||||
pub episode_id: i32,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
pub enum Relation {
|
||||
#[sea_orm(
|
||||
belongs_to = "super::subscriptions::Entity",
|
||||
from = "Column::SubscriptionId",
|
||||
to = "super::subscriptions::Column::Id",
|
||||
on_update = "Cascade",
|
||||
on_delete = "Cascade"
|
||||
)]
|
||||
Subscription,
|
||||
#[sea_orm(
|
||||
belongs_to = "super::episodes::Entity",
|
||||
from = "Column::EpisodeId",
|
||||
to = "super::episodes::Column::Id",
|
||||
on_update = "Cascade",
|
||||
on_delete = "Cascade"
|
||||
)]
|
||||
Episode,
|
||||
}
|
||||
|
||||
impl Related<super::subscriptions::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::Subscription.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::episodes::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::Episode.def()
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
||||
|
||||
impl ActiveModel {
|
||||
pub fn from_subscription_and_episode(subscription_id: i32, episode_id: i32) -> Self {
|
||||
Self {
|
||||
subscription_id: ActiveValue::Set(subscription_id),
|
||||
episode_id: ActiveValue::Set(episode_id),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
@ -5,7 +5,6 @@ use loco_rs::app::AppContext;
|
||||
use sea_orm::{entity::prelude::*, ActiveValue};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
pub use super::entities::subscriptions::{self, *};
|
||||
use super::{bangumi, episodes, query::filter_values_in};
|
||||
use crate::{
|
||||
app::AppContextExt,
|
||||
@ -24,6 +23,92 @@ use crate::{
|
||||
models::episodes::MikanEpsiodeCreation,
|
||||
};
|
||||
|
||||
#[derive(
|
||||
Clone, Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum, Serialize, Deserialize, DeriveDisplay,
|
||||
)]
|
||||
#[sea_orm(
|
||||
rs_type = "String",
|
||||
db_type = "Enum",
|
||||
enum_name = "subscription_category"
|
||||
)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum SubscriptionCategory {
|
||||
#[sea_orm(string_value = "mikan")]
|
||||
Mikan,
|
||||
#[sea_orm(string_value = "manual")]
|
||||
Manual,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
|
||||
#[sea_orm(table_name = "subscriptions")]
|
||||
pub struct Model {
|
||||
#[sea_orm(column_type = "Timestamp")]
|
||||
pub created_at: DateTime,
|
||||
#[sea_orm(column_type = "Timestamp")]
|
||||
pub updated_at: DateTime,
|
||||
#[sea_orm(primary_key)]
|
||||
pub id: i32,
|
||||
pub display_name: String,
|
||||
pub subscriber_id: i32,
|
||||
pub category: SubscriptionCategory,
|
||||
pub source_url: String,
|
||||
pub enabled: bool,
|
||||
}
|
||||
|
||||
#[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::bangumi::Entity")]
|
||||
Bangumi,
|
||||
#[sea_orm(has_many = "super::episodes::Entity")]
|
||||
Episodes,
|
||||
#[sea_orm(has_many = "super::subscription_episode::Entity")]
|
||||
SubscriptionEpisode,
|
||||
#[sea_orm(has_many = "super::subscription_bangumi::Entity")]
|
||||
SubscriptionBangumi,
|
||||
}
|
||||
|
||||
impl Related<super::subscribers::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::Subscriber.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::bangumi::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
super::subscription_bangumi::Relation::Bangumi.def()
|
||||
}
|
||||
|
||||
fn via() -> Option<RelationDef> {
|
||||
Some(
|
||||
super::subscription_bangumi::Relation::Subscription
|
||||
.def()
|
||||
.rev(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::episodes::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
super::subscription_episode::Relation::Episode.def()
|
||||
}
|
||||
|
||||
fn via() -> Option<RelationDef> {
|
||||
Some(
|
||||
super::subscription_episode::Relation::Subscription
|
||||
.def()
|
||||
.rev(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct SubscriptionCreateFromRssDto {
|
||||
pub rss_link: String,
|
||||
@ -77,7 +162,7 @@ impl Model {
|
||||
Ok(subscription.insert(db).await?)
|
||||
}
|
||||
|
||||
pub async fn toggle_iters(
|
||||
pub async fn toggle_with_ids(
|
||||
ctx: &AppContext,
|
||||
ids: impl Iterator<Item = i32>,
|
||||
enabled: bool,
|
||||
@ -91,7 +176,7 @@ impl Model {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn delete_iters(
|
||||
pub async fn delete_with_ids(
|
||||
ctx: &AppContext,
|
||||
ids: impl Iterator<Item = i32>,
|
||||
) -> eyre::Result<()> {
|
||||
@ -213,6 +298,7 @@ impl Model {
|
||||
);
|
||||
episodes::Model::add_episodes(
|
||||
ctx,
|
||||
self.id,
|
||||
new_ep_metas.into_iter().map(|item| MikanEpsiodeCreation {
|
||||
episode: item,
|
||||
bangumi: bgm.clone(),
|
||||
|
@ -1,6 +1,6 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::models::entities::subscribers;
|
||||
use crate::models::subscribers;
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct CurrentResponse {
|
||||
|
Loading…
Reference in New Issue
Block a user