tetratto_core2/database/
apps.rs1use oiseau::cache::Cache;
2use crate::model::{
3 apps::{ThirdPartyApp},
4 auth::User,
5 oauth::AppScope,
6 permissions::FinePermission,
7 Error, Result,
8};
9use crate::{auto_method, DataManager};
10use oiseau::{PostgresRow, execute, get, query_row, query_rows, params};
11
12impl DataManager {
13 pub(crate) fn get_app_from_row(x: &PostgresRow) -> ThirdPartyApp {
15 ThirdPartyApp {
16 id: crate::model::id::Id::deserialize(&get!(x->0(String))),
17 created: get!(x->1(i64)) as u128,
18 owner: crate::model::id::Id::deserialize(&get!(x->2(String))),
19 title: get!(x->3(String)),
20 homepage: get!(x->4(String)),
21 redirect: get!(x->5(String)),
22 banned: get!(x->6(i32)) as i8 == 1,
23 grants: get!(x->7(i32)) as usize,
24 scopes: serde_json::from_str(&get!(x->8(String))).unwrap(),
25 api_key: get!(x->9(String)),
26 }
27 }
28
29 auto_method!(get_app_by_id()@get_app_from_row -> "SELECT * FROM apps WHERE id = $1" --name="app" --returns=ThirdPartyApp --cache-key-tmpl="atto.app:{}");
30 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:{}");
31
32 pub async fn get_apps_by_owner(&self, id: &crate::model::id::Id) -> Result<Vec<ThirdPartyApp>> {
37 let conn = match self.0.connect().await {
38 Ok(c) => c,
39 Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
40 };
41
42 let res = query_rows!(
43 &conn,
44 "SELECT * FROM apps WHERE owner = $1 ORDER BY created DESC",
45 &[&id.printable()],
46 |x| { Self::get_app_from_row(x) }
47 );
48
49 if res.is_err() {
50 return Err(Error::GeneralNotFound("app".to_string()));
51 }
52
53 Ok(res.unwrap())
54 }
55
56 const MAXIMUM_FREE_APPS: usize = 1;
57
58 pub async fn create_app(&self, data: ThirdPartyApp) -> Result<ThirdPartyApp> {
63 if data.title.trim().len() < 2 {
65 return Err(Error::DataTooShort("title".to_string()));
66 } else if data.title.len() > 32 {
67 return Err(Error::DataTooLong("title".to_string()));
68 }
69
70 let owner = self.get_user_by_id(&data.owner).await?;
72
73 if !owner.permissions.check(FinePermission::Supporter) {
74 let apps = self
75 .get_table_row_count_where("apps", &format!("owner = {}", owner.id))
76 .await? as usize;
77
78 if apps >= Self::MAXIMUM_FREE_APPS {
79 return Err(Error::MiscError(
80 "You already have the maximum number of apps you can have".to_string(),
81 ));
82 }
83 }
84
85 let conn = match self.0.connect().await {
87 Ok(c) => c,
88 Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
89 };
90
91 let res = execute!(
92 &conn,
93 "INSERT INTO apps VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)",
94 params![
95 &data.id.printable(),
96 &(data.created as i64),
97 &data.owner.printable(),
98 &data.title,
99 &data.homepage,
100 &data.redirect,
101 &{ if data.banned { 1 } else { 0 } },
102 &(data.grants as i32),
103 &serde_json::to_string(&data.scopes).unwrap(),
104 &data.api_key,
105 ]
106 );
107
108 if let Err(e) = res {
109 return Err(Error::DatabaseError(e.to_string()));
110 }
111
112 Ok(data)
113 }
114
115 pub async fn delete_app(&self, id: &crate::model::id::Id, user: &User) -> Result<()> {
116 let app = self.get_app_by_id(id).await?;
117
118 if user.id != app.owner && !user.permissions.check(FinePermission::ManageApps) {
120 return Err(Error::NotAllowed);
121 }
122
123 let conn = match self.0.connect().await {
125 Ok(c) => c,
126 Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
127 };
128
129 let res = execute!(&conn, "DELETE FROM apps WHERE id = $1", &[&id.printable()]);
130
131 if let Err(e) = res {
132 return Err(Error::DatabaseError(e.to_string()));
133 }
134
135 self.cache_clear_app(&app).await;
136
137 let res = execute!(
139 &conn,
140 "DELETE FROM app_data WHERE app = $1",
141 &[&id.printable()]
142 );
143
144 if let Err(e) = res {
145 return Err(Error::DatabaseError(e.to_string()));
146 }
147
148 Ok(())
150 }
151
152 pub async fn cache_clear_app(&self, app: &ThirdPartyApp) {
153 self.0.1.remove(format!("atto.app:{}", app.id)).await;
154 self.0.1.remove(format!("atto.app_k:{}", app.api_key)).await;
155 }
156
157 auto_method!(update_app_title(&str)@get_app_by_id:FinePermission::ManageApps; -> "UPDATE apps SET title = $1 WHERE id = $2" --cache-key-tmpl=cache_clear_app);
158 auto_method!(update_app_homepage(&str)@get_app_by_id:FinePermission::ManageApps; -> "UPDATE apps SET homepage = $1 WHERE id = $2" --cache-key-tmpl=cache_clear_app);
159 auto_method!(update_app_redirect(&str)@get_app_by_id:FinePermission::ManageApps; -> "UPDATE apps SET redirect = $1 WHERE id = $2" --cache-key-tmpl=cache_clear_app);
160 auto_method!(update_app_scopes(Vec<AppScope>)@get_app_by_id:FinePermission::ManageApps; -> "UPDATE apps SET scopes = $1 WHERE id = $2" --serde --cache-key-tmpl=cache_clear_app);
161 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);
162
163 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);
164 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);
165
166 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);
167 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);
168}