2020-03-19 22:19:05 +00:00
|
|
|
use crate::error::MyError;
|
2020-03-16 17:56:26 +00:00
|
|
|
use activitystreams::primitives::XsdAnyUri;
|
2020-03-19 22:19:05 +00:00
|
|
|
use bb8_postgres::{
|
|
|
|
bb8,
|
2020-03-20 00:55:11 +00:00
|
|
|
tokio_postgres::{
|
|
|
|
error::{Error, SqlState},
|
|
|
|
row::Row,
|
|
|
|
Client, Config, NoTls,
|
|
|
|
},
|
2020-03-19 22:19:05 +00:00
|
|
|
PostgresConnectionManager,
|
|
|
|
};
|
2020-03-18 22:37:22 +00:00
|
|
|
use log::{info, warn};
|
2020-03-16 17:56:26 +00:00
|
|
|
use rsa::RSAPrivateKey;
|
|
|
|
use rsa_pem::KeyExt;
|
2020-03-19 22:19:05 +00:00
|
|
|
use std::{collections::HashSet, convert::TryInto};
|
2020-03-16 17:56:26 +00:00
|
|
|
|
2020-03-19 22:19:05 +00:00
|
|
|
pub type Pool = bb8::Pool<PostgresConnectionManager<NoTls>>;
|
2020-03-16 17:56:26 +00:00
|
|
|
|
2020-03-19 22:19:05 +00:00
|
|
|
#[derive(Clone)]
|
|
|
|
pub struct Db {
|
|
|
|
pool: Pool,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Db {
|
2020-03-22 23:21:40 +00:00
|
|
|
pub async fn build(config: &crate::config::Config) -> Result<Self, MyError> {
|
|
|
|
let cpus: u32 = num_cpus::get().try_into()?;
|
|
|
|
let max_conns = cpus * config.connections_per_core();
|
|
|
|
|
|
|
|
let config: Config = config.database_url().parse()?;
|
2020-03-19 22:19:05 +00:00
|
|
|
let manager = PostgresConnectionManager::new(config, NoTls);
|
|
|
|
|
|
|
|
let pool = bb8::Pool::builder()
|
2020-03-22 23:21:40 +00:00
|
|
|
.max_size(max_conns)
|
2020-03-19 22:19:05 +00:00
|
|
|
.build(manager)
|
|
|
|
.await?;
|
|
|
|
|
|
|
|
Ok(Db { pool })
|
|
|
|
}
|
|
|
|
|
2020-03-22 21:18:36 +00:00
|
|
|
pub fn pool(&self) -> &Pool {
|
|
|
|
&self.pool
|
|
|
|
}
|
|
|
|
|
2020-03-19 22:19:05 +00:00
|
|
|
pub async fn remove_listener(&self, inbox: XsdAnyUri) -> Result<(), MyError> {
|
|
|
|
let conn = self.pool.get().await?;
|
|
|
|
|
|
|
|
remove_listener(&conn, &inbox).await?;
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn add_listener(&self, inbox: XsdAnyUri) -> Result<(), MyError> {
|
|
|
|
let conn = self.pool.get().await?;
|
|
|
|
|
|
|
|
add_listener(&conn, &inbox).await?;
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2020-03-20 00:55:11 +00:00
|
|
|
pub async fn add_blocks(&self, domains: &[String]) -> Result<(), MyError> {
|
|
|
|
let conn = self.pool.get().await?;
|
|
|
|
for domain in domains {
|
|
|
|
match add_block(&conn, domain.as_str()).await {
|
|
|
|
Err(e) if e.code() != Some(&SqlState::UNIQUE_VIOLATION) => {
|
|
|
|
Err(e)?;
|
|
|
|
}
|
|
|
|
_ => (),
|
|
|
|
};
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn remove_blocks(&self, domains: &[String]) -> Result<(), MyError> {
|
|
|
|
let conn = self.pool.get().await?;
|
|
|
|
for domain in domains {
|
|
|
|
remove_block(&conn, domain.as_str()).await?
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn add_whitelists(&self, domains: &[String]) -> Result<(), MyError> {
|
|
|
|
let conn = self.pool.get().await?;
|
|
|
|
for domain in domains {
|
|
|
|
match add_whitelist(&conn, domain.as_str()).await {
|
|
|
|
Err(e) if e.code() != Some(&SqlState::UNIQUE_VIOLATION) => {
|
|
|
|
Err(e)?;
|
|
|
|
}
|
|
|
|
_ => (),
|
|
|
|
};
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn remove_whitelists(&self, domains: &[String]) -> Result<(), MyError> {
|
|
|
|
let conn = self.pool.get().await?;
|
|
|
|
for domain in domains {
|
|
|
|
remove_whitelist(&conn, domain.as_str()).await?
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2020-03-19 22:19:05 +00:00
|
|
|
pub async fn hydrate_blocks(&self) -> Result<HashSet<String>, MyError> {
|
|
|
|
let conn = self.pool.get().await?;
|
|
|
|
|
|
|
|
Ok(hydrate_blocks(&conn).await?)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn hydrate_whitelists(&self) -> Result<HashSet<String>, MyError> {
|
|
|
|
let conn = self.pool.get().await?;
|
|
|
|
|
|
|
|
Ok(hydrate_whitelists(&conn).await?)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn hydrate_listeners(&self) -> Result<HashSet<XsdAnyUri>, MyError> {
|
|
|
|
let conn = self.pool.get().await?;
|
|
|
|
|
|
|
|
Ok(hydrate_listeners(&conn).await?)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn hydrate_private_key(&self) -> Result<Option<RSAPrivateKey>, MyError> {
|
|
|
|
let conn = self.pool.get().await?;
|
|
|
|
|
|
|
|
Ok(hydrate_private_key(&conn).await?)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn update_private_key(&self, private_key: &RSAPrivateKey) -> Result<(), MyError> {
|
|
|
|
let conn = self.pool.get().await?;
|
|
|
|
|
|
|
|
Ok(update_private_key(&conn, private_key).await?)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-20 00:55:11 +00:00
|
|
|
pub async fn listen(client: &Client) -> Result<(), Error> {
|
2020-03-16 18:27:42 +00:00
|
|
|
info!("LISTEN new_blocks;");
|
|
|
|
info!("LISTEN new_whitelists;");
|
|
|
|
info!("LISTEN new_listeners;");
|
|
|
|
info!("LISTEN rm_blocks;");
|
|
|
|
info!("LISTEN rm_whitelists;");
|
|
|
|
info!("LISTEN rm_listeners;");
|
2020-03-16 17:56:26 +00:00
|
|
|
client
|
|
|
|
.batch_execute(
|
|
|
|
"LISTEN new_blocks;
|
|
|
|
LISTEN new_whitelists;
|
|
|
|
LISTEN new_listeners;
|
|
|
|
LISTEN rm_blocks;
|
|
|
|
LISTEN rm_whitelists;
|
|
|
|
LISTEN rm_listeners;",
|
|
|
|
)
|
|
|
|
.await?;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2020-03-19 22:19:05 +00:00
|
|
|
async fn hydrate_private_key(client: &Client) -> Result<Option<RSAPrivateKey>, MyError> {
|
2020-03-16 17:56:26 +00:00
|
|
|
info!("SELECT value FROM settings WHERE key = 'private_key'");
|
|
|
|
let rows = client
|
|
|
|
.query("SELECT value FROM settings WHERE key = 'private_key'", &[])
|
|
|
|
.await?;
|
|
|
|
|
|
|
|
if let Some(row) = rows.into_iter().next() {
|
|
|
|
let key_str: String = row.get(0);
|
|
|
|
return Ok(Some(KeyExt::from_pem_pkcs8(&key_str)?));
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(None)
|
|
|
|
}
|
|
|
|
|
2020-03-19 22:19:05 +00:00
|
|
|
async fn update_private_key(client: &Client, key: &RSAPrivateKey) -> Result<(), MyError> {
|
2020-03-16 17:56:26 +00:00
|
|
|
let pem_pkcs8 = key.to_pem_pkcs8()?;
|
|
|
|
|
|
|
|
info!("INSERT INTO settings (key, value, created_at) VALUES ('private_key', $1::TEXT, 'now');");
|
|
|
|
client.execute("INSERT INTO settings (key, value, created_at) VALUES ('private_key', $1::TEXT, 'now');", &[&pem_pkcs8]).await?;
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2020-03-20 00:55:11 +00:00
|
|
|
async fn add_block(client: &Client, domain: &str) -> Result<(), Error> {
|
2020-03-16 17:56:26 +00:00
|
|
|
info!(
|
|
|
|
"INSERT INTO blocks (domain_name, created_at) VALUES ($1::TEXT, 'now'); [{}]",
|
2020-03-20 00:55:11 +00:00
|
|
|
domain,
|
2020-03-16 17:56:26 +00:00
|
|
|
);
|
|
|
|
client
|
|
|
|
.execute(
|
|
|
|
"INSERT INTO blocks (domain_name, created_at) VALUES ($1::TEXT, 'now');",
|
2020-03-20 00:55:11 +00:00
|
|
|
&[&domain],
|
2020-03-16 17:56:26 +00:00
|
|
|
)
|
|
|
|
.await?;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2020-03-20 00:55:11 +00:00
|
|
|
async fn remove_block(client: &Client, domain: &str) -> Result<(), Error> {
|
|
|
|
info!(
|
|
|
|
"DELETE FROM blocks WHERE domain_name = $1::TEXT; [{}]",
|
|
|
|
domain,
|
|
|
|
);
|
|
|
|
client
|
|
|
|
.execute(
|
|
|
|
"DELETE FROM blocks WHERE domain_name = $1::TEXT;",
|
|
|
|
&[&domain],
|
|
|
|
)
|
|
|
|
.await?;
|
2020-03-16 17:56:26 +00:00
|
|
|
|
2020-03-20 00:55:11 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
async fn add_whitelist(client: &Client, domain: &str) -> Result<(), Error> {
|
2020-03-16 17:56:26 +00:00
|
|
|
info!(
|
|
|
|
"INSERT INTO whitelists (domain_name, created_at) VALUES ($1::TEXT, 'now'); [{}]",
|
2020-03-20 00:55:11 +00:00
|
|
|
domain,
|
2020-03-16 17:56:26 +00:00
|
|
|
);
|
|
|
|
client
|
|
|
|
.execute(
|
|
|
|
"INSERT INTO whitelists (domain_name, created_at) VALUES ($1::TEXT, 'now');",
|
2020-03-20 00:55:11 +00:00
|
|
|
&[&domain],
|
|
|
|
)
|
|
|
|
.await?;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
async fn remove_whitelist(client: &Client, domain: &str) -> Result<(), Error> {
|
|
|
|
info!(
|
|
|
|
"DELETE FROM whitelists WHERE domain_name = $1::TEXT; [{}]",
|
|
|
|
domain,
|
|
|
|
);
|
|
|
|
client
|
|
|
|
.execute(
|
|
|
|
"DELETE FROM whitelists WHERE domain_name = $1::TEXT;",
|
|
|
|
&[&domain],
|
2020-03-16 17:56:26 +00:00
|
|
|
)
|
|
|
|
.await?;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2020-03-20 00:55:11 +00:00
|
|
|
async fn remove_listener(client: &Client, listener: &XsdAnyUri) -> Result<(), Error> {
|
2020-03-16 17:56:26 +00:00
|
|
|
info!(
|
|
|
|
"DELETE FROM listeners WHERE actor_id = {};",
|
|
|
|
listener.as_str()
|
|
|
|
);
|
|
|
|
client
|
|
|
|
.execute(
|
|
|
|
"DELETE FROM listeners WHERE actor_id = $1::TEXT;",
|
|
|
|
&[&listener.as_str()],
|
|
|
|
)
|
|
|
|
.await?;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2020-03-20 00:55:11 +00:00
|
|
|
async fn add_listener(client: &Client, listener: &XsdAnyUri) -> Result<(), Error> {
|
2020-03-16 17:56:26 +00:00
|
|
|
info!(
|
|
|
|
"INSERT INTO listeners (actor_id, created_at) VALUES ($1::TEXT, 'now'); [{}]",
|
|
|
|
listener.as_str(),
|
|
|
|
);
|
|
|
|
client
|
|
|
|
.execute(
|
|
|
|
"INSERT INTO listeners (actor_id, created_at) VALUES ($1::TEXT, 'now');",
|
|
|
|
&[&listener.as_str()],
|
|
|
|
)
|
|
|
|
.await?;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2020-03-20 00:55:11 +00:00
|
|
|
async fn hydrate_blocks(client: &Client) -> Result<HashSet<String>, Error> {
|
2020-03-16 17:56:26 +00:00
|
|
|
info!("SELECT domain_name FROM blocks");
|
|
|
|
let rows = client.query("SELECT domain_name FROM blocks", &[]).await?;
|
|
|
|
|
|
|
|
parse_rows(rows)
|
|
|
|
}
|
|
|
|
|
2020-03-20 00:55:11 +00:00
|
|
|
async fn hydrate_whitelists(client: &Client) -> Result<HashSet<String>, Error> {
|
2020-03-16 17:56:26 +00:00
|
|
|
info!("SELECT domain_name FROM whitelists");
|
|
|
|
let rows = client
|
|
|
|
.query("SELECT domain_name FROM whitelists", &[])
|
|
|
|
.await?;
|
|
|
|
|
|
|
|
parse_rows(rows)
|
|
|
|
}
|
|
|
|
|
2020-03-20 00:55:11 +00:00
|
|
|
async fn hydrate_listeners(client: &Client) -> Result<HashSet<XsdAnyUri>, Error> {
|
2020-03-16 17:56:26 +00:00
|
|
|
info!("SELECT actor_id FROM listeners");
|
|
|
|
let rows = client.query("SELECT actor_id FROM listeners", &[]).await?;
|
|
|
|
|
|
|
|
parse_rows(rows)
|
|
|
|
}
|
|
|
|
|
2020-03-20 00:55:11 +00:00
|
|
|
fn parse_rows<T, E>(rows: Vec<Row>) -> Result<HashSet<T>, Error>
|
2020-03-16 17:56:26 +00:00
|
|
|
where
|
2020-03-18 22:37:22 +00:00
|
|
|
T: std::str::FromStr<Err = E> + Eq + std::hash::Hash,
|
|
|
|
E: std::fmt::Display,
|
2020-03-16 17:56:26 +00:00
|
|
|
{
|
|
|
|
let hs = rows
|
|
|
|
.into_iter()
|
2020-03-18 22:37:22 +00:00
|
|
|
.filter_map(move |row| match row.try_get::<_, String>(0) {
|
|
|
|
Ok(s) => match s.parse() {
|
|
|
|
Ok(t) => Some(t),
|
|
|
|
Err(e) => {
|
|
|
|
warn!("Couln't parse row, '{}', {}", s, e);
|
|
|
|
None
|
|
|
|
}
|
|
|
|
},
|
|
|
|
Err(e) => {
|
|
|
|
warn!("Couldn't get column, {}", e);
|
|
|
|
None
|
|
|
|
}
|
2020-03-16 17:56:26 +00:00
|
|
|
})
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
Ok(hs)
|
|
|
|
}
|