diff --git a/packages/backend/native-utils/Cargo.lock b/packages/backend/native-utils/Cargo.lock index dd0a41ea74..71206507bd 100644 --- a/packages/backend/native-utils/Cargo.lock +++ b/packages/backend/native-utils/Cargo.lock @@ -577,6 +577,21 @@ dependencies = [ "libc", ] +[[package]] +name = "crc" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86ec7a15cbe22e59248fc7eadb1907dab5ba09372595da4d73dd805ed4417dfe" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cace84e55f07e7301bae1c519df89cdad8cc3cd868413d3fdbdeca9ff3db484" + [[package]] name = "crossbeam-queue" version = "0.3.8" @@ -2447,8 +2462,10 @@ dependencies = [ "scylla", "serde", "serde_yaml", + "sqlx", "thiserror", "tokio", + "urlencoding", ] [[package]] @@ -2869,6 +2886,7 @@ dependencies = [ "byteorder", "bytes", "chrono", + "crc", "crossbeam-queue", "dirs", "dotenvy", @@ -2929,6 +2947,7 @@ dependencies = [ "proc-macro2", "quote", "serde_json", + "sha2", "sqlx-core", "sqlx-rt", "syn 1.0.109", @@ -3396,9 +3415,9 @@ dependencies = [ [[package]] name = "urlencoding" -version = "2.1.2" +version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8db7427f936968176eaa7cdf81b7f98b980b18495ec28f1b5791ac3bfe3eea9" +checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" [[package]] name = "utf8parse" diff --git a/packages/backend/native-utils/scylla-migration/Cargo.toml b/packages/backend/native-utils/scylla-migration/Cargo.toml index d8a2f0db05..1197de08c3 100644 --- a/packages/backend/native-utils/scylla-migration/Cargo.toml +++ b/packages/backend/native-utils/scylla-migration/Cargo.toml @@ -11,5 +11,7 @@ clap = { version = "4.3.11", features = ["derive"] } scylla = "0.8.2" serde = { version = "1.0.171", features = ["derive"] } serde_yaml = "0.9.22" +sqlx = { version = "0.6.0", features = ["runtime-tokio-rustls", "postgres"] } thiserror = "1.0.43" tokio = { version = "1.29.1", features = ["full"] } +urlencoding = "2.1.3" diff --git a/packages/backend/native-utils/scylla-migration/src/cli.rs b/packages/backend/native-utils/scylla-migration/src/cli.rs index 12d4590a18..ff177b015c 100644 --- a/packages/backend/native-utils/scylla-migration/src/cli.rs +++ b/packages/backend/native-utils/scylla-migration/src/cli.rs @@ -23,16 +23,16 @@ pub async fn run_cli() -> Result<(), Error> { let yml = fs::File::open(cli.config.expect("Path to 'default.yml' not specified")) .expect("Failed to open 'default.yml'"); let config: Config = serde_yaml::from_reader(yml).expect("Failed to parse yaml"); - let config = config + let scylla_conf = config .scylla .expect("ScyllaDB config not found in 'default.yml'"); match cli.subcommand { MigrationCommand::Up { num } => { - Migrator::new(migration_dir, &config).await?.up(num).await? + Migrator::new(migration_dir, &scylla_conf).await?.up(num).await? } MigrationCommand::Down { num } => { - Migrator::new(migration_dir, &config) + Migrator::new(migration_dir, &scylla_conf) .await? .down(num) .await? @@ -106,4 +106,6 @@ pub(crate) enum MigrationCommand { )] num: u32, }, + #[clap(about = "Set up PostgreSQL and ScyllaDB", display_order = 40)] + Setup, } diff --git a/packages/backend/native-utils/scylla-migration/src/config.rs b/packages/backend/native-utils/scylla-migration/src/config.rs index 0737059263..0064d9d2dd 100644 --- a/packages/backend/native-utils/scylla-migration/src/config.rs +++ b/packages/backend/native-utils/scylla-migration/src/config.rs @@ -4,6 +4,7 @@ use serde::Deserialize; #[serde(rename_all = "camelCase")] pub struct Config { pub scylla: Option, + pub db: DbConfig, } #[derive(Debug, PartialEq, Deserialize)] @@ -13,3 +14,12 @@ pub struct ScyllaConfig { pub keyspace: String, pub replication_factor: i32, } + +#[derive(Debug, PartialEq, Deserialize)] +pub struct DbConfig { + pub host: String, + pub port: u32, + pub db: String, + pub user: String, + pub pass: String, +} diff --git a/packages/backend/native-utils/scylla-migration/src/error.rs b/packages/backend/native-utils/scylla-migration/src/error.rs index 11c0a4c0b1..074c611af3 100644 --- a/packages/backend/native-utils/scylla-migration/src/error.rs +++ b/packages/backend/native-utils/scylla-migration/src/error.rs @@ -20,4 +20,6 @@ pub enum Error { Row(#[from] SingleRowTypedError), #[error("File error: {0}")] File(#[from] io::Error), + #[error("PostgreSQL error: {0}")] + Postgres(#[from] sqlx::Error), } diff --git a/packages/backend/native-utils/scylla-migration/src/lib.rs b/packages/backend/native-utils/scylla-migration/src/lib.rs index 878200531e..856d870c0c 100644 --- a/packages/backend/native-utils/scylla-migration/src/lib.rs +++ b/packages/backend/native-utils/scylla-migration/src/lib.rs @@ -5,3 +5,4 @@ pub mod config; pub mod error; pub(crate) mod migrator; +pub(crate) mod setup; diff --git a/packages/backend/native-utils/scylla-migration/src/setup.rs b/packages/backend/native-utils/scylla-migration/src/setup.rs new file mode 100644 index 0000000000..196258c712 --- /dev/null +++ b/packages/backend/native-utils/scylla-migration/src/setup.rs @@ -0,0 +1,64 @@ +use scylla::{Session, SessionBuilder}; +use sqlx::{Connection, PgConnection}; +use urlencoding::encode; + +use crate::{ + config::{DbConfig, ScyllaConfig}, + error::Error, +}; + +pub(crate) struct Initializer { + scylla: Session, + postgres_url: String, +} + +impl Initializer { + pub(crate) async fn new( + scylla_conf: &ScyllaConfig, + postgres_conf: &DbConfig, + ) -> Result { + let session = SessionBuilder::new() + .known_nodes(&scylla_conf.nodes) + .build() + .await?; + + let conn_url = format!( + "postgres://{}:{}@{}:{}/{}", + postgres_conf.user, + encode(&postgres_conf.pass), + postgres_conf.host, + postgres_conf.port, + postgres_conf.db, + ); + + Ok(Self { + scylla: session, + postgres_url: conn_url, + }) + } + + pub(crate) async fn setup(&self) -> Result<(), Error> { + const DROP_CONSTRAINT: &str = "ALTER TABLE ? DROP CONSTRAINT ?"; + + let pairs = vec![ + ("channel_note_pining", "FK_10b19ef67d297ea9de325cd4502"), + ("clip_note", "FK_a012eaf5c87c65da1deb5fdbfa3"), + ("muted_note", "FK_70ab9786313d78e4201d81cdb89"), + ("note_edit", "FK_702ad5ae993a672e4fbffbcd38c"), + ("note_favorite", "FK_0e00498f180193423c992bc4370"), + ("note_unread", "FK_e637cba4dc4410218c4251260e4"), + ("note_wathing", "FK_03e7028ab8388a3f5e3ce2a8619"), + ("promo_note", "FK_e263909ca4fe5d57f8d4230dd5c"), + ("promo_read", "FK_a46a1a603ecee695d7db26da5f4"), + ("user_note_pining", "FK_68881008f7c3588ad7ecae471cf") + ]; + + let mut conn = PgConnection::connect(&self.postgres_url).await?; + + for (table, fk) in pairs { + sqlx::query(DROP_CONSTRAINT).bind(table).bind(fk).execute(&mut conn).await?; + } + + Ok(()) + } +} diff --git a/packages/backend/src/server/api/endpoints/i/get-word-muted-notes-count.ts b/packages/backend/src/server/api/endpoints/i/get-word-muted-notes-count.ts index bd58f9257a..65a3303fcf 100644 --- a/packages/backend/src/server/api/endpoints/i/get-word-muted-notes-count.ts +++ b/packages/backend/src/server/api/endpoints/i/get-word-muted-notes-count.ts @@ -1,3 +1,4 @@ +import { scyllaClient } from "@/db/scylla.js"; import define from "../../define.js"; import { MutedNotes } from "@/models/index.js"; @@ -29,6 +30,10 @@ export const paramDef = { } as const; export default define(meta, paramDef, async (ps, user) => { + if (scyllaClient) { + return { count: -1 }; + } + return { count: await MutedNotes.countBy({ userId: user.id,