use oiseau::cache::Cache;
use crate::model::{
apps::{AppQuota, ThirdPartyApp, DeveloperPassStorageQuota},
auth::User,
oauth::AppScope,
permissions::{FinePermission, SecondaryPermission},
Error, Result,
};
use crate::{auto_method, DataManager};
use oiseau::{PostgresRow, execute, get, query_row, query_rows, params};
impl DataManager {
pub(crate) fn get_app_from_row(x: &PostgresRow) -> ThirdPartyApp {
ThirdPartyApp {
id: get!(x->0(i64)) as usize,
created: get!(x->1(i64)) as usize,
owner: get!(x->2(i64)) as usize,
title: get!(x->3(String)),
homepage: get!(x->4(String)),
redirect: get!(x->5(String)),
quota_status: serde_json::from_str(&get!(x->6(String))).unwrap(),
banned: get!(x->7(i32)) as i8 == 1,
grants: get!(x->8(i32)) as usize,
scopes: serde_json::from_str(&get!(x->9(String))).unwrap(),
api_key: get!(x->10(String)),
data_used: get!(x->11(i32)) as usize,
storage_capacity: serde_json::from_str(&get!(x->12(String))).unwrap(),
}
}
auto_method!(get_app_by_id(usize as i64)@get_app_from_row -> "SELECT * FROM apps WHERE id = $1" --name="app" --returns=ThirdPartyApp --cache-key-tmpl="atto.app:{}");
auto_method!(get_app_by_api_key(&str)@get_app_from_row -> "SELECT * FROM apps WHERE api_key = $1" --name="app" --returns=ThirdPartyApp --cache-key-tmpl="atto.app_k:{}");
pub async fn get_apps_by_owner(&self, id: usize) -> Result<Vec<ThirdPartyApp>> {
let conn = match self.0.connect().await {
Ok(c) => c,
Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
};
let res = query_rows!(
&conn,
"SELECT * FROM apps WHERE owner = $1 ORDER BY created DESC",
&[&(id as i64)],
|x| { Self::get_app_from_row(x) }
);
if res.is_err() {
return Err(Error::GeneralNotFound("app".to_string()));
}
Ok(res.unwrap())
}
const MAXIMUM_FREE_APPS: usize = 1;
pub async fn create_app(&self, data: ThirdPartyApp) -> Result<ThirdPartyApp> {
if data.title.trim().len() < 2 {
return Err(Error::DataTooShort("title".to_string()));
} else if data.title.len() > 32 {
return Err(Error::DataTooLong("title".to_string()));
}
let owner = self.get_user_by_id(data.owner).await?;
if !owner
.secondary_permissions
.check(SecondaryPermission::DEVELOPER_PASS)
{
let apps = self
.get_table_row_count_where("apps", &format!("owner = {}", owner.id))
.await? as usize;
if apps >= Self::MAXIMUM_FREE_APPS {
return Err(Error::MiscError(
"You already have the maximum number of apps you can have".to_string(),
));
}
}
let conn = match self.0.connect().await {
Ok(c) => c,
Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
};
let res = execute!(
&conn,
"INSERT INTO apps VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)",
params![
&(data.id as i64),
&(data.created as i64),
&(data.owner as i64),
&data.title,
&data.homepage,
&data.redirect,
&serde_json::to_string(&data.quota_status).unwrap(),
&{ if data.banned { 1 } else { 0 } },
&(data.grants as i32),
&serde_json::to_string(&data.scopes).unwrap(),
&data.api_key,
&(data.data_used as i32),
&serde_json::to_string(&data.storage_capacity).unwrap(),
]
);
if let Err(e) = res {
return Err(Error::DatabaseError(e.to_string()));
}
Ok(data)
}
pub async fn delete_app(&self, id: usize, user: &User) -> Result<()> {
let app = self.get_app_by_id(id).await?;
if user.id != app.owner && !user.permissions.check(FinePermission::MANAGE_APPS) {
return Err(Error::NotAllowed);
}
let conn = match self.0.connect().await {
Ok(c) => c,
Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
};
let res = execute!(&conn, "DELETE FROM apps WHERE id = $1", &[&(id as i64)]);
if let Err(e) = res {
return Err(Error::DatabaseError(e.to_string()));
}
self.cache_clear_app(&app).await;
let res = execute!(
&conn,
"DELETE FROM app_data WHERE app = $1",
&[&(id as i64)]
);
if let Err(e) = res {
return Err(Error::DatabaseError(e.to_string()));
}
Ok(())
}
pub async fn cache_clear_app(&self, app: &ThirdPartyApp) {
self.0.1.remove(format!("atto.app:{}", app.id)).await;
self.0.1.remove(format!("atto.app_k:{}", app.api_key)).await;
}
auto_method!(update_app_title(&str)@get_app_by_id:FinePermission::MANAGE_APPS; -> "UPDATE apps SET title = $1 WHERE id = $2" --cache-key-tmpl=cache_clear_app);
auto_method!(update_app_homepage(&str)@get_app_by_id:FinePermission::MANAGE_APPS; -> "UPDATE apps SET homepage = $1 WHERE id = $2" --cache-key-tmpl=cache_clear_app);
auto_method!(update_app_redirect(&str)@get_app_by_id:FinePermission::MANAGE_APPS; -> "UPDATE apps SET redirect = $1 WHERE id = $2" --cache-key-tmpl=cache_clear_app);
auto_method!(update_app_quota_status(AppQuota)@get_app_by_id -> "UPDATE apps SET quota_status = $1 WHERE id = $2" --serde --cache-key-tmpl=cache_clear_app);
auto_method!(update_app_scopes(Vec<AppScope>)@get_app_by_id:FinePermission::MANAGE_APPS; -> "UPDATE apps SET scopes = $1 WHERE id = $2" --serde --cache-key-tmpl=cache_clear_app);
auto_method!(update_app_api_key(&str)@get_app_by_id -> "UPDATE apps SET api_key = $1 WHERE id = $2" --cache-key-tmpl=cache_clear_app);
auto_method!(update_app_storage_capacity(DeveloperPassStorageQuota)@get_app_by_id -> "UPDATE apps SET storage_capacity = $1 WHERE id = $2" --serde --cache-key-tmpl=cache_clear_app);
auto_method!(update_app_data_used(i32)@get_app_by_id -> "UPDATE apps SET data_used = $1 WHERE id = $2" --cache-key-tmpl=cache_clear_app);
auto_method!(add_app_data_used(i32)@get_app_by_id -> "UPDATE apps SET data_used = data_used + $1 WHERE id = $2" --cache-key-tmpl=cache_clear_app);
auto_method!(incr_app_grants()@get_app_by_id -> "UPDATE apps SET grants = grants + 1 WHERE id = $1" --cache-key-tmpl=cache_clear_app --incr);
auto_method!(decr_app_grants()@get_app_by_id -> "UPDATE apps SET grants = grants - 1 WHERE id = $1" --cache-key-tmpl=cache_clear_app --decr=grants);
}