add: stripe integration
This commit is contained in:
parent
2fa5a4dc1f
commit
1d120555a0
31 changed files with 1137 additions and 122 deletions
|
@ -45,6 +45,7 @@ impl DataManager {
|
|||
post_count: get!(x->15(i32)) as usize,
|
||||
request_count: get!(x->16(i32)) as usize,
|
||||
connections: serde_json::from_str(&get!(x->17(String)).to_string()).unwrap(),
|
||||
stripe_id: get!(x->18(String)),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -139,7 +140,7 @@ impl DataManager {
|
|||
|
||||
let res = execute!(
|
||||
&conn,
|
||||
"INSERT INTO users VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18)",
|
||||
"INSERT INTO users VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19)",
|
||||
params![
|
||||
&(data.id as i64),
|
||||
&(data.created as i64),
|
||||
|
@ -159,6 +160,7 @@ impl DataManager {
|
|||
&0_i32,
|
||||
&0_i32,
|
||||
&serde_json::to_string(&data.connections).unwrap(),
|
||||
&""
|
||||
]
|
||||
);
|
||||
|
||||
|
@ -433,24 +435,27 @@ impl DataManager {
|
|||
id: usize,
|
||||
role: FinePermission,
|
||||
user: User,
|
||||
force: bool,
|
||||
) -> Result<()> {
|
||||
// check permission
|
||||
if !user.permissions.check(FinePermission::MANAGE_USERS) {
|
||||
return Err(Error::NotAllowed);
|
||||
}
|
||||
|
||||
let other_user = self.get_user_by_id(id).await?;
|
||||
|
||||
if other_user.permissions.check_manager() && !user.permissions.check_admin() {
|
||||
return Err(Error::MiscError(
|
||||
"Cannot manage the role of other managers".to_string(),
|
||||
));
|
||||
}
|
||||
if !force {
|
||||
// check permission
|
||||
if !user.permissions.check(FinePermission::MANAGE_USERS) {
|
||||
return Err(Error::NotAllowed);
|
||||
}
|
||||
|
||||
if other_user.permissions == user.permissions {
|
||||
return Err(Error::MiscError(
|
||||
"Cannot manage users of equal level to you".to_string(),
|
||||
));
|
||||
if other_user.permissions.check_manager() && !user.permissions.check_admin() {
|
||||
return Err(Error::MiscError(
|
||||
"Cannot manage the role of other managers".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
if other_user.permissions == user.permissions {
|
||||
return Err(Error::MiscError(
|
||||
"Cannot manage users of equal level to you".to_string(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// ...
|
||||
|
@ -645,6 +650,9 @@ impl DataManager {
|
|||
auto_method!(update_user_connections(UserConnections)@get_user_by_id -> "UPDATE users SET connections = $1 WHERE id = $2" --serde --cache-key-tmpl=cache_clear_user);
|
||||
auto_method!(update_user_subscriptions(HashMap<usize, usize>)@get_user_by_id -> "UPDATE users SET subscriptions = $1 WHERE id = $2" --serde --cache-key-tmpl=cache_clear_user);
|
||||
|
||||
auto_method!(get_user_by_stripe_id(&str)@get_user_from_row -> "SELECT * FROM users WHERE stripe_id = $1" --name="user" --returns=User);
|
||||
auto_method!(update_user_stripe_id(&str)@get_user_by_id -> "UPDATE users SET stripe_id = $1 WHERE id = $2" --cache-key-tmpl=cache_clear_user);
|
||||
|
||||
auto_method!(incr_user_notifications()@get_user_by_id -> "UPDATE users SET notification_count = notification_count + 1 WHERE id = $1" --cache-key-tmpl=cache_clear_user --incr);
|
||||
auto_method!(decr_user_notifications()@get_user_by_id -> "UPDATE users SET notification_count = notification_count - 1 WHERE id = $1" --cache-key-tmpl=cache_clear_user --decr);
|
||||
|
||||
|
|
|
@ -30,6 +30,8 @@ impl DataManager {
|
|||
execute!(&conn, common::CREATE_TABLE_IPBLOCKS).unwrap();
|
||||
execute!(&conn, common::CREATE_TABLE_CHANNELS).unwrap();
|
||||
execute!(&conn, common::CREATE_TABLE_MESSAGES).unwrap();
|
||||
execute!(&conn, common::CREATE_TABLE_UPLOADS).unwrap();
|
||||
execute!(&conn, common::CREATE_TABLE_EMOJIS).unwrap();
|
||||
|
||||
self.2
|
||||
.set("atto.active_connections:users".to_string(), "0".to_string())
|
||||
|
|
|
@ -15,3 +15,5 @@ pub const CREATE_TABLE_QUESTIONS: &str = include_str!("./sql/create_questions.sq
|
|||
pub const CREATE_TABLE_IPBLOCKS: &str = include_str!("./sql/create_ipblocks.sql");
|
||||
pub const CREATE_TABLE_CHANNELS: &str = include_str!("./sql/create_channels.sql");
|
||||
pub const CREATE_TABLE_MESSAGES: &str = include_str!("./sql/create_messages.sql");
|
||||
pub const CREATE_TABLE_UPLOADS: &str = include_str!("./sql/create_uploads.sql");
|
||||
pub const CREATE_TABLE_EMOJIS: &str = include_str!("./sql/create_emojis.sql");
|
||||
|
|
8
crates/core/src/database/drivers/sql/create_emojis.sql
Normal file
8
crates/core/src/database/drivers/sql/create_emojis.sql
Normal file
|
@ -0,0 +1,8 @@
|
|||
CREATE TABLE IF NOT EXISTS emojis (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
created BIGINT NOT NULL,
|
||||
owner BIGINT NOT NULL,
|
||||
community BIGINT NOT NULL,
|
||||
upload_id BIGINT NOT NULL,
|
||||
name TEXT NOT NULL
|
||||
)
|
6
crates/core/src/database/drivers/sql/create_uploads.sql
Normal file
6
crates/core/src/database/drivers/sql/create_uploads.sql
Normal file
|
@ -0,0 +1,6 @@
|
|||
CREATE TABLE IF NOT EXISTS uploads (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
created BIGINT NOT NULL,
|
||||
owner BIGINT NOT NULL,
|
||||
what TEXT NOT NULL
|
||||
)
|
191
crates/core/src/database/emojis.rs
Normal file
191
crates/core/src/database/emojis.rs
Normal file
|
@ -0,0 +1,191 @@
|
|||
use super::*;
|
||||
use crate::cache::Cache;
|
||||
use crate::model::{
|
||||
Error, Result, auth::User, permissions::FinePermission,
|
||||
communities_permissions::CommunityPermission, uploads::CustomEmoji,
|
||||
};
|
||||
use crate::{auto_method, execute, get, query_row, query_rows, params};
|
||||
|
||||
#[cfg(feature = "sqlite")]
|
||||
use rusqlite::Row;
|
||||
|
||||
#[cfg(feature = "postgres")]
|
||||
use tokio_postgres::Row;
|
||||
|
||||
impl DataManager {
|
||||
/// Get a [`CustomEmoji`] from an SQL row.
|
||||
pub(crate) fn get_emoji_from_row(
|
||||
#[cfg(feature = "sqlite")] x: &Row<'_>,
|
||||
#[cfg(feature = "postgres")] x: &Row,
|
||||
) -> CustomEmoji {
|
||||
CustomEmoji {
|
||||
id: get!(x->0(i64)) as usize,
|
||||
owner: get!(x->1(i64)) as usize,
|
||||
created: get!(x->2(i64)) as usize,
|
||||
community: get!(x->3(i64)) as usize,
|
||||
upload_id: get!(x->4(i64)) as usize,
|
||||
name: get!(x->5(String)),
|
||||
}
|
||||
}
|
||||
|
||||
auto_method!(get_emoji_by_id(usize as i64)@get_emoji_from_row -> "SELECT * FROM emojis WHERE id = $1" --name="emoji" --returns=CustomEmoji --cache-key-tmpl="atto.emoji:{}");
|
||||
|
||||
/// Get all emojis by community.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `community` - the ID of the community to fetch emojis for
|
||||
pub async fn get_emojis_by_community(&self, community: usize) -> Result<Vec<CustomEmoji>> {
|
||||
let conn = match self.connect().await {
|
||||
Ok(c) => c,
|
||||
Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
|
||||
};
|
||||
|
||||
let res = query_rows!(
|
||||
&conn,
|
||||
"SELECT * FROM emojis WHERE community = $1 ORDER BY name ASC",
|
||||
&[&(community as i64)],
|
||||
|x| { Self::get_emoji_from_row(x) }
|
||||
);
|
||||
|
||||
if res.is_err() {
|
||||
return Err(Error::GeneralNotFound("emoji".to_string()));
|
||||
}
|
||||
|
||||
Ok(res.unwrap())
|
||||
}
|
||||
|
||||
/// Get all emojis by user.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `user` - the ID of the user to fetch emojis for
|
||||
pub async fn get_emojis_by_user(&self, user: usize) -> Result<Vec<CustomEmoji>> {
|
||||
let conn = match self.connect().await {
|
||||
Ok(c) => c,
|
||||
Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
|
||||
};
|
||||
|
||||
let res = query_rows!(
|
||||
&conn,
|
||||
"SELECT * FROM emojis WHERE (owner = $1 OR members LIKE $2) AND community = 0 ORDER BY created DESC",
|
||||
params![&(user as i64), &format!("%{user}%")],
|
||||
|x| { Self::get_emoji_from_row(x) }
|
||||
);
|
||||
|
||||
if res.is_err() {
|
||||
return Err(Error::GeneralNotFound("emoji".to_string()));
|
||||
}
|
||||
|
||||
Ok(res.unwrap())
|
||||
}
|
||||
|
||||
/// Get a emoji given its `owner` and a member.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `owner` - the ID of the owner
|
||||
/// * `member` - the ID of the member
|
||||
pub async fn get_emoji_by_owner_member(
|
||||
&self,
|
||||
owner: usize,
|
||||
member: usize,
|
||||
) -> Result<CustomEmoji> {
|
||||
let conn = match self.connect().await {
|
||||
Ok(c) => c,
|
||||
Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
|
||||
};
|
||||
|
||||
let res = query_row!(
|
||||
&conn,
|
||||
"SELECT * FROM emojis WHERE owner = $1 AND members = $2 AND community = 0 ORDER BY created DESC",
|
||||
params![&(owner as i64), &format!("[{member}]")],
|
||||
|x| { Ok(Self::get_emoji_from_row(x)) }
|
||||
);
|
||||
|
||||
if res.is_err() {
|
||||
return Err(Error::GeneralNotFound("emoji".to_string()));
|
||||
}
|
||||
|
||||
Ok(res.unwrap())
|
||||
}
|
||||
|
||||
/// Create a new emoji in the database.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `data` - a mock [`CustomEmoji`] object to insert
|
||||
pub async fn create_emoji(&self, data: CustomEmoji) -> Result<()> {
|
||||
let user = self.get_user_by_id(data.owner).await?;
|
||||
|
||||
// check user permission in community
|
||||
if data.community != 0 {
|
||||
let membership = self
|
||||
.get_membership_by_owner_community(user.id, data.community)
|
||||
.await?;
|
||||
|
||||
if !membership.role.check(CommunityPermission::MANAGE_EMOJIS)
|
||||
&& !user.permissions.check(FinePermission::MANAGE_EMOJIS)
|
||||
{
|
||||
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,
|
||||
"INSERT INTO emojis VALUES ($1, $2, $3, $4, $5, $6)",
|
||||
params![
|
||||
&(data.id as i64),
|
||||
&(data.owner as i64),
|
||||
&(data.created as i64),
|
||||
&(data.community as i64),
|
||||
&(data.upload_id as i64),
|
||||
&data.name
|
||||
]
|
||||
);
|
||||
|
||||
if let Err(e) = res {
|
||||
return Err(Error::DatabaseError(e.to_string()));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn delete_emoji(&self, id: usize, user: &User) -> Result<()> {
|
||||
let emoji = self.get_emoji_by_id(id).await?;
|
||||
|
||||
// check user permission in community
|
||||
if user.id != emoji.owner {
|
||||
let membership = self
|
||||
.get_membership_by_owner_community(user.id, emoji.community)
|
||||
.await?;
|
||||
|
||||
if !membership.role.check(CommunityPermission::MANAGE_EMOJIS) {
|
||||
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, "DELETE FROM emojis WHERE id = $1", &[&(id as i64)]);
|
||||
|
||||
if let Err(e) = res {
|
||||
return Err(Error::DatabaseError(e.to_string()));
|
||||
}
|
||||
|
||||
// delete uploads
|
||||
self.delete_upload(emoji.upload_id).await?;
|
||||
|
||||
// ...
|
||||
self.2.remove(format!("atto.emoji:{}", id)).await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
auto_method!(update_emoji_name(&str)@get_emoji_by_id:MANAGE_EMOJIS -> "UPDATE emojis SET title = $1 WHERE id = $2" --cache-key-tmpl="atto.emoji:{}");
|
||||
}
|
|
@ -4,6 +4,7 @@ mod common;
|
|||
mod communities;
|
||||
pub mod connections;
|
||||
mod drivers;
|
||||
mod emojis;
|
||||
mod ipbans;
|
||||
mod ipblocks;
|
||||
mod memberships;
|
||||
|
@ -13,6 +14,7 @@ mod questions;
|
|||
mod reactions;
|
||||
mod reports;
|
||||
mod requests;
|
||||
mod uploads;
|
||||
mod user_warnings;
|
||||
mod userblocks;
|
||||
mod userfollows;
|
||||
|
|
122
crates/core/src/database/uploads.rs
Normal file
122
crates/core/src/database/uploads.rs
Normal file
|
@ -0,0 +1,122 @@
|
|||
use std::fs::{exists, remove_file};
|
||||
use super::*;
|
||||
use crate::cache::Cache;
|
||||
use crate::model::{Error, Result, uploads::MediaUpload};
|
||||
use crate::{auto_method, execute, get, query_row, query_rows, params};
|
||||
|
||||
use pathbufd::PathBufD;
|
||||
#[cfg(feature = "sqlite")]
|
||||
use rusqlite::Row;
|
||||
|
||||
#[cfg(feature = "postgres")]
|
||||
use tokio_postgres::Row;
|
||||
|
||||
impl DataManager {
|
||||
/// Get a [`MediaUpload`] from an SQL row.
|
||||
pub(crate) fn get_upload_from_row(
|
||||
#[cfg(feature = "sqlite")] x: &Row<'_>,
|
||||
#[cfg(feature = "postgres")] x: &Row,
|
||||
) -> MediaUpload {
|
||||
MediaUpload {
|
||||
id: get!(x->0(i64)) as usize,
|
||||
created: get!(x->1(i64)) as usize,
|
||||
owner: get!(x->2(i64)) as usize,
|
||||
what: serde_json::from_str(&get!(x->3(String))).unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
auto_method!(get_upload_by_id(usize as i64)@get_upload_from_row -> "SELECT * FROM uploads WHERE id = $1" --name="upload" --returns=MediaUpload --cache-key-tmpl="atto.uploads:{}");
|
||||
|
||||
/// Get all uploads (paginated).
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `batch` - the limit of items in each page
|
||||
/// * `page` - the page number
|
||||
pub async fn get_uploads(&self, batch: usize, page: usize) -> Result<Vec<MediaUpload>> {
|
||||
let conn = match self.connect().await {
|
||||
Ok(c) => c,
|
||||
Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
|
||||
};
|
||||
|
||||
let res = query_rows!(
|
||||
&conn,
|
||||
"SELECT * FROM uploads ORDER BY created DESC LIMIT $1 OFFSET $2",
|
||||
&[&(batch as i64), &((page * batch) as i64)],
|
||||
|x| { Self::get_upload_from_row(x) }
|
||||
);
|
||||
|
||||
if res.is_err() {
|
||||
return Err(Error::GeneralNotFound("upload".to_string()));
|
||||
}
|
||||
|
||||
Ok(res.unwrap())
|
||||
}
|
||||
|
||||
/// Create a new upload in the database.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `data` - a mock [`MediaUpload`] object to insert
|
||||
pub async fn create_upload(&self, data: MediaUpload) -> Result<()> {
|
||||
let conn = match self.connect().await {
|
||||
Ok(c) => c,
|
||||
Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
|
||||
};
|
||||
|
||||
let res = execute!(
|
||||
&conn,
|
||||
"INSERT INTO uploads VALUES ($1, $2, $3, $4)",
|
||||
params![
|
||||
&(data.id as i64),
|
||||
&(data.created as i64),
|
||||
&(data.owner as i64),
|
||||
&serde_json::to_string(&data.what).unwrap().as_str(),
|
||||
]
|
||||
);
|
||||
|
||||
if let Err(e) = res {
|
||||
return Err(Error::DatabaseError(e.to_string()));
|
||||
}
|
||||
|
||||
// return
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn delete_upload(&self, id: usize) -> Result<()> {
|
||||
// if !user.permissions.check(FinePermission::MANAGE_UPLOADS) {
|
||||
// return Err(Error::NotAllowed);
|
||||
// }
|
||||
|
||||
// delete file
|
||||
// it's most important that the file gets off the file system first, even
|
||||
// if there's an issue in the database
|
||||
//
|
||||
// the actual file takes up much more space than the database entry.
|
||||
let path = PathBufD::current().extend(&[self.0.dirs.media.as_str(), "uploads"]);
|
||||
if let Ok(exists) = exists(&path) {
|
||||
if exists {
|
||||
if let Err(e) = remove_file(&path) {
|
||||
return Err(Error::MiscError(e.to_string()));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return Err(Error::GeneralNotFound("file".to_string()));
|
||||
}
|
||||
|
||||
// delete from database
|
||||
let conn = match self.connect().await {
|
||||
Ok(c) => c,
|
||||
Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
|
||||
};
|
||||
|
||||
let res = execute!(&conn, "DELETE FROM uploads WHERE id = $1", &[&(id as i64)]);
|
||||
|
||||
if let Err(e) = res {
|
||||
return Err(Error::DatabaseError(e.to_string()));
|
||||
}
|
||||
|
||||
self.2.remove(format!("atto.upload:{}", id)).await;
|
||||
|
||||
// return
|
||||
Ok(())
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue