add: redis cache support
This commit is contained in:
parent
1d9a96ae69
commit
38dbf10130
13 changed files with 541 additions and 17 deletions
|
@ -1,4 +1,5 @@
|
|||
use super::*;
|
||||
use crate::cache::Cache;
|
||||
use crate::model::{
|
||||
Error, Result,
|
||||
auth::{Token, User},
|
||||
|
@ -31,8 +32,8 @@ impl DataManager {
|
|||
}
|
||||
}
|
||||
|
||||
auto_method!(get_user_by_id(&str)@get_user_from_row -> "SELECT * FROM users WHERE id = $1" --name="user" --returns=User);
|
||||
auto_method!(get_user_by_username(&str)@get_user_from_row -> "SELECT * FROM users WHERE username = $1" --name="user" --returns=User);
|
||||
auto_method!(get_user_by_id(&str)@get_user_from_row -> "SELECT * FROM users WHERE id = $1" --name="user" --returns=User --cache-key-tmpl="atto.user:{}");
|
||||
auto_method!(get_user_by_username(&str)@get_user_from_row -> "SELECT * FROM users WHERE username = $1" --name="user" --returns=User --cache-key-tmpl="atto.user:{}");
|
||||
|
||||
/// Get a user given just their auth token.
|
||||
///
|
||||
|
@ -130,8 +131,11 @@ impl DataManager {
|
|||
return Err(Error::DatabaseError(e.to_string()));
|
||||
}
|
||||
|
||||
self.2.remove(format!("atto.user:{}", id)).await;
|
||||
self.2.remove(format!("atto.user:{}", user.username)).await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
auto_method!(update_user_tokens(Vec<Token>) -> "UPDATE users SET tokens = $1 WHERE id = $2" --serde);
|
||||
auto_method!(update_user_tokens(Vec<Token>) -> "UPDATE users SET tokens = $1 WHERE id = $2" --serde --cache-key-tmpl="atto.user:{}");
|
||||
}
|
||||
|
|
|
@ -39,6 +39,31 @@ macro_rules! auto_method {
|
|||
}
|
||||
};
|
||||
|
||||
($name:ident()@$select_fn:ident -> $query:literal --name=$name_:literal --returns=$returns_:tt --cache-key-tmpl=$cache_key_tmpl:literal) => {
|
||||
pub async fn $name(&self, id: usize) -> Result<$returns_> {
|
||||
let conn = match self.connect().await {
|
||||
Ok(c) => c,
|
||||
Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
|
||||
};
|
||||
|
||||
let res = query_row!(&conn, $query, &[&id], |x| { Ok(Self::$select_fn(x)) });
|
||||
|
||||
if res.is_err() {
|
||||
return Err(Error::GeneralNotFound($name_.to_string()));
|
||||
}
|
||||
|
||||
let x = res.unwrap();
|
||||
self.2
|
||||
.set(
|
||||
format!($cache_key_tmpl, id),
|
||||
serde_json::to_string(&x).unwrap(),
|
||||
)
|
||||
.await;
|
||||
|
||||
Ok(x)
|
||||
}
|
||||
};
|
||||
|
||||
($name:ident($selector_t:ty)@$select_fn:ident -> $query:literal --name=$name_:literal --returns=$returns_:tt) => {
|
||||
pub async fn $name(&self, selector: $selector_t) -> Result<$returns_> {
|
||||
let conn = match self.connect().await {
|
||||
|
@ -56,6 +81,31 @@ macro_rules! auto_method {
|
|||
}
|
||||
};
|
||||
|
||||
($name:ident($selector_t:ty)@$select_fn:ident -> $query:literal --name=$name_:literal --returns=$returns_:tt --cache-key-tmpl=$cache_key_tmpl:literal) => {
|
||||
pub async fn $name(&self, selector: $selector_t) -> Result<$returns_> {
|
||||
let conn = match self.connect().await {
|
||||
Ok(c) => c,
|
||||
Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
|
||||
};
|
||||
|
||||
let res = query_row!(&conn, $query, &[&selector], |x| { Ok(Self::$select_fn(x)) });
|
||||
|
||||
if res.is_err() {
|
||||
return Err(Error::GeneralNotFound($name_.to_string()));
|
||||
}
|
||||
|
||||
let x = res.unwrap();
|
||||
self.2
|
||||
.set(
|
||||
format!($cache_key_tmpl, selector),
|
||||
serde_json::to_string(&x).unwrap(),
|
||||
)
|
||||
.await;
|
||||
|
||||
Ok(x)
|
||||
}
|
||||
};
|
||||
|
||||
($name:ident()@$select_fn:ident:$permission:ident -> $query:literal) => {
|
||||
pub async fn $name(&self, id: usize, user: User) -> Result<()> {
|
||||
let page = self.$select_fn(id).await?;
|
||||
|
@ -81,6 +131,33 @@ macro_rules! auto_method {
|
|||
}
|
||||
};
|
||||
|
||||
($name:ident()@$select_fn:ident:$permission:ident -> $query:literal --cache-key-tmpl=$cache_key_tmpl:literal) => {
|
||||
pub async fn $name(&self, id: usize, user: User) -> Result<()> {
|
||||
let page = self.$select_fn(id).await?;
|
||||
|
||||
if user.id != page.owner {
|
||||
if !user.permissions.check(FinePermission::$permission) {
|
||||
return Err(Error::NotAllowed);
|
||||
}
|
||||
}
|
||||
|
||||
let conn = match self.connect().await {
|
||||
Ok(c) => c,
|
||||
Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
|
||||
};
|
||||
|
||||
let res = execute!(&conn, $query, &[&id.to_string()]);
|
||||
|
||||
if let Err(e) = res {
|
||||
return Err(Error::DatabaseError(e.to_string()));
|
||||
}
|
||||
|
||||
self.2.remove(format!($cache_key_tmpl, id)).await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
};
|
||||
|
||||
($name:ident($x:ty)@$select_fn:ident:$permission:ident -> $query:literal) => {
|
||||
pub async fn $name(&self, id: usize, user: User, x: $x) -> Result<()> {
|
||||
let y = self.$select_fn(id).await?;
|
||||
|
@ -106,6 +183,33 @@ macro_rules! auto_method {
|
|||
}
|
||||
};
|
||||
|
||||
($name:ident($x:ty)@$select_fn:ident:$permission:ident -> $query:literal --cache-key-tmpl=$cache_key_tmpl:literal) => {
|
||||
pub async fn $name(&self, id: usize, user: User, x: $x) -> Result<()> {
|
||||
let y = self.$select_fn(id).await?;
|
||||
|
||||
if user.id != y.owner {
|
||||
if !user.permissions.check(FinePermission::$permission) {
|
||||
return Err(Error::NotAllowed);
|
||||
}
|
||||
}
|
||||
|
||||
let conn = match self.connect().await {
|
||||
Ok(c) => c,
|
||||
Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
|
||||
};
|
||||
|
||||
let res = execute!(&conn, $query, &[&x, &id.to_string()]);
|
||||
|
||||
if let Err(e) = res {
|
||||
return Err(Error::DatabaseError(e.to_string()));
|
||||
}
|
||||
|
||||
self.2.remove(format!($cache_key_tmpl, id)).await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
};
|
||||
|
||||
($name:ident($x:ty)@$select_fn:ident:$permission:ident -> $query:literal --serde) => {
|
||||
pub async fn $name(&self, id: usize, user: User, x: $x) -> Result<()> {
|
||||
let y = self.$select_fn(id).await?;
|
||||
|
@ -135,6 +239,37 @@ macro_rules! auto_method {
|
|||
}
|
||||
};
|
||||
|
||||
($name:ident($x:ty)@$select_fn:ident:$permission:ident -> $query:literal --serde --cache-key-tmpl=$cache_key_tmpl:literal) => {
|
||||
pub async fn $name(&self, id: usize, user: User, x: $x) -> Result<()> {
|
||||
let y = self.$select_fn(id).await?;
|
||||
|
||||
if user.id != y.owner {
|
||||
if !user.permissions.check(FinePermission::$permission) {
|
||||
return Err(Error::NotAllowed);
|
||||
}
|
||||
}
|
||||
|
||||
let conn = match self.connect().await {
|
||||
Ok(c) => c,
|
||||
Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
|
||||
};
|
||||
|
||||
let res = execute!(
|
||||
&conn,
|
||||
$query,
|
||||
&[&serde_json::to_string(&x).unwrap(), &id.to_string()]
|
||||
);
|
||||
|
||||
if let Err(e) = res {
|
||||
return Err(Error::DatabaseError(e.to_string()));
|
||||
}
|
||||
|
||||
self.2.remove(format!($cache_key_tmpl, id)).await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
};
|
||||
|
||||
($name:ident($x:ty) -> $query:literal) => {
|
||||
pub async fn $name(&self, id: usize, x: $x) -> Result<()> {
|
||||
let conn = match self.connect().await {
|
||||
|
@ -152,6 +287,25 @@ macro_rules! auto_method {
|
|||
}
|
||||
};
|
||||
|
||||
($name:ident($x:ty) -> $query:literal --cache-key-tmpl=$cache_key_tmpl:literal) => {
|
||||
pub async fn $name(&self, id: usize, x: $x) -> Result<()> {
|
||||
let conn = match self.connect().await {
|
||||
Ok(c) => c,
|
||||
Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
|
||||
};
|
||||
|
||||
let res = execute!(&conn, $query, &[&x, &id.to_string()]);
|
||||
|
||||
if let Err(e) = res {
|
||||
return Err(Error::DatabaseError(e.to_string()));
|
||||
}
|
||||
|
||||
self.2.remove(format!($cache_key_tmpl, id)).await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
};
|
||||
|
||||
($name:ident($x:ty) -> $query:literal --serde) => {
|
||||
pub async fn $name(&self, id: usize, x: $x) -> Result<()> {
|
||||
let conn = match self.connect().await {
|
||||
|
@ -172,4 +326,27 @@ macro_rules! auto_method {
|
|||
Ok(())
|
||||
}
|
||||
};
|
||||
|
||||
($name:ident($x:ty) -> $query:literal --serde --cache-key-tmpl=$cache_key_tmpl:literal) => {
|
||||
pub async fn $name(&self, id: usize, x: $x) -> Result<()> {
|
||||
let conn = match self.connect().await {
|
||||
Ok(c) => c,
|
||||
Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
|
||||
};
|
||||
|
||||
let res = execute!(
|
||||
&conn,
|
||||
$query,
|
||||
&[&serde_json::to_string(&x).unwrap(), &id.to_string()]
|
||||
);
|
||||
|
||||
if let Err(e) = res {
|
||||
return Err(Error::DatabaseError(e.to_string()));
|
||||
}
|
||||
|
||||
self.2.remove(format!($cache_key_tmpl, id)).await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,3 +1,10 @@
|
|||
#[cfg(not(feature = "redis"))]
|
||||
use crate::cache::no_cache::NoCache;
|
||||
#[cfg(feature = "redis")]
|
||||
use crate::cache::redis::RedisCache;
|
||||
|
||||
use crate::cache::Cache;
|
||||
|
||||
use crate::config::Config;
|
||||
use bb8_postgres::{
|
||||
PostgresConnectionManager,
|
||||
|
@ -13,13 +20,15 @@ pub type Connection<'a> = PooledConnection<'a, PostgresConnectionManager<NoTls>>
|
|||
pub struct DataManager(
|
||||
pub Config,
|
||||
pub HashMap<String, LangFile>,
|
||||
#[cfg(feature = "redis")] pub RedisCache,
|
||||
#[cfg(not(feature = "redis"))] pub NoCache,
|
||||
pub Pool<PostgresConnectionManager<NoTls>>,
|
||||
);
|
||||
|
||||
impl DataManager {
|
||||
/// Obtain a connection to the staging database.
|
||||
pub(crate) async fn connect(&self) -> Result<Connection> {
|
||||
Ok(self.2.get().await.unwrap())
|
||||
Ok(self.3.get().await.unwrap())
|
||||
}
|
||||
|
||||
/// Create a new [`DataManager`] (and init database).
|
||||
|
@ -36,7 +45,15 @@ impl DataManager {
|
|||
);
|
||||
|
||||
let pool = Pool::builder().max_size(15).build(manager).await.unwrap();
|
||||
Ok(Self(config.clone(), read_langs(), pool))
|
||||
Ok(Self(
|
||||
config.clone(),
|
||||
read_langs(),
|
||||
#[cfg(feature = "redis")]
|
||||
RedisCache::new().await,
|
||||
#[cfg(not(feature = "redis"))]
|
||||
NoCache::new().await,
|
||||
pool,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,10 +1,22 @@
|
|||
#[cfg(not(feature = "redis"))]
|
||||
use crate::cache::no_cache::NoCache;
|
||||
#[cfg(feature = "redis")]
|
||||
use crate::cache::redis::RedisCache;
|
||||
|
||||
use crate::cache::Cache;
|
||||
|
||||
use crate::config::Config;
|
||||
use rusqlite::{Connection, Result};
|
||||
use std::collections::HashMap;
|
||||
use tetratto_l10n::{LangFile, read_langs};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct DataManager(pub Config, pub HashMap<String, LangFile>);
|
||||
pub struct DataManager(
|
||||
pub Config,
|
||||
pub HashMap<String, LangFile>,
|
||||
#[cfg(feature = "redis")] pub RedisCache,
|
||||
#[cfg(not(feature = "redis"))] pub NoCache,
|
||||
);
|
||||
|
||||
impl DataManager {
|
||||
/// Obtain a connection to the staging database.
|
||||
|
@ -14,7 +26,14 @@ impl DataManager {
|
|||
|
||||
/// Create a new [`DataManager`] (and init database).
|
||||
pub async fn new(config: Config) -> Result<Self> {
|
||||
let this = Self(config.clone(), read_langs());
|
||||
let this = Self(
|
||||
config.clone(),
|
||||
read_langs(),
|
||||
#[cfg(feature = "redis")]
|
||||
RedisCache::new().await,
|
||||
#[cfg(not(feature = "redis"))]
|
||||
NoCache::new().await,
|
||||
);
|
||||
|
||||
let conn = this.connect().await?;
|
||||
conn.pragma_update(None, "journal_mode", "WAL").unwrap();
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use super::*;
|
||||
use crate::cache::Cache;
|
||||
use crate::model::auth::User;
|
||||
use crate::model::{Error, Result, journal::JournalPage, permissions::FinePermission};
|
||||
use crate::{auto_method, execute, get, query_row};
|
||||
|
@ -26,7 +27,7 @@ impl DataManager {
|
|||
}
|
||||
}
|
||||
|
||||
auto_method!(get_page_by_id()@get_page_from_row -> "SELECT * FROM pages WHERE id = $1" --name="journal page" --returns=JournalPage);
|
||||
auto_method!(get_page_by_id()@get_page_from_row -> "SELECT * FROM pages WHERE id = $1" --name="journal page" --returns=JournalPage --cache-key-tmpl="atto.page:{}");
|
||||
|
||||
/// Create a new journal page in the database.
|
||||
///
|
||||
|
@ -77,9 +78,9 @@ impl DataManager {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
auto_method!(delete_page()@get_page_by_id:MANAGE_JOURNAL_PAGES -> "DELETE FROM pages WHERE id = $1");
|
||||
auto_method!(update_page_title(String)@get_page_by_id:MANAGE_JOURNAL_PAGES -> "UPDATE pages SET title = $1 WHERE id = $2");
|
||||
auto_method!(update_page_prompt(String)@get_page_by_id:MANAGE_JOURNAL_PAGES -> "UPDATE pages SET prompt = $1 WHERE id = $2");
|
||||
auto_method!(update_page_read_access(String)@get_page_by_id:MANAGE_JOURNAL_PAGES -> "UPDATE pages SET read_access = $1 WHERE id = $2" --serde);
|
||||
auto_method!(update_page_write_access(String)@get_page_by_id:MANAGE_JOURNAL_PAGES -> "UPDATE pages SET write_access = $1 WHERE id = $2" --serde);
|
||||
auto_method!(delete_page()@get_page_by_id:MANAGE_JOURNAL_PAGES -> "DELETE FROM pages WHERE id = $1" --cache-key-tmpl="atto.page:{}");
|
||||
auto_method!(update_page_title(String)@get_page_by_id:MANAGE_JOURNAL_PAGES -> "UPDATE pages SET title = $1 WHERE id = $2" --cache-key-tmpl="atto.page:{}");
|
||||
auto_method!(update_page_prompt(String)@get_page_by_id:MANAGE_JOURNAL_PAGES -> "UPDATE pages SET prompt = $1 WHERE id = $2" --cache-key-tmpl="atto.page:{}");
|
||||
auto_method!(update_page_read_access(String)@get_page_by_id:MANAGE_JOURNAL_PAGES -> "UPDATE pages SET read_access = $1 WHERE id = $2" --serde --cache-key-tmpl="atto.page:{}");
|
||||
auto_method!(update_page_write_access(String)@get_page_by_id:MANAGE_JOURNAL_PAGES -> "UPDATE pages SET write_access = $1 WHERE id = $2" --serde --cache-key-tmpl="atto.page:{}");
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue