tetratto_core/database/
app_data.rs1use oiseau::cache::Cache;
2use crate::model::apps::{AppDataQuery, AppDataQueryResult, AppDataSelectMode};
3use crate::model::{apps::AppData, permissions::FinePermission, Error, Result};
4use crate::{auto_method, DataManager};
5use oiseau::{PostgresRow, execute, get, query_row, query_rows, params};
6
7pub const FREE_DATA_LIMIT: usize = 512_000;
8pub const PASS_DATA_LIMIT: usize = 26_214_400;
9
10impl DataManager {
11 pub(crate) fn get_app_data_from_row(x: &PostgresRow) -> AppData {
13 AppData {
14 id: get!(x->0(i64)) as usize,
15 app: get!(x->1(i64)) as usize,
16 key: get!(x->2(String)),
17 value: get!(x->3(String)),
18 }
19 }
20
21 auto_method!(get_app_data_by_id(usize as i64)@get_app_data_from_row -> "SELECT * FROM app_data WHERE id = $1" --name="app_data" --returns=AppData --cache-key-tmpl="atto.app_data:{}");
22
23 pub async fn get_app_data_by_app(&self, id: usize) -> Result<Vec<AppData>> {
28 let conn = match self.0.connect().await {
29 Ok(c) => c,
30 Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
31 };
32
33 let res = query_rows!(
34 &conn,
35 "SELECT * FROM app_data WHERE app = $1 ORDER BY created DESC",
36 &[&(id as i64)],
37 |x| { Self::get_app_data_from_row(x) }
38 );
39
40 if res.is_err() {
41 return Err(Error::GeneralNotFound("app_data".to_string()));
42 }
43
44 Ok(res.unwrap())
45 }
46
47 pub async fn query_app_data(&self, query: AppDataQuery) -> Result<AppDataQueryResult> {
49 let conn = match self.0.connect().await {
50 Ok(c) => c,
51 Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
52 };
53
54 let query_str = query.to_string().replace("%q%", &query.query.selector());
55
56 let res = match query.mode {
57 AppDataSelectMode::One(_) => AppDataQueryResult::One(
58 match query_row!(&conn, &query_str, params![&query.query.to_string()], |x| {
59 Ok(Self::get_app_data_from_row(x))
60 }) {
61 Ok(x) => x,
62 Err(_) => return Err(Error::GeneralNotFound("app_data".to_string())),
63 },
64 ),
65 AppDataSelectMode::Many(_, _) => AppDataQueryResult::Many(
66 match query_rows!(&conn, &query_str, params![&query.query.to_string()], |x| {
67 Self::get_app_data_from_row(x)
68 }) {
69 Ok(x) => x,
70 Err(_) => return Err(Error::GeneralNotFound("app_data".to_string())),
71 },
72 ),
73 AppDataSelectMode::ManyJson(_, _, _) => AppDataQueryResult::Many(
74 match query_rows!(&conn, &query_str, params![&query.query.to_string()], |x| {
75 Self::get_app_data_from_row(x)
76 }) {
77 Ok(x) => x,
78 Err(_) => return Err(Error::GeneralNotFound("app_data".to_string())),
79 },
80 ),
81 };
82
83 Ok(res)
84 }
85
86 pub async fn query_delete_app_data(&self, query: AppDataQuery) -> Result<()> {
88 let conn = match self.0.connect().await {
89 Ok(c) => c,
90 Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
91 };
92
93 let query_str = query
94 .to_string()
95 .replace("%q%", &query.query.selector())
96 .replace("SELECT * FROM", "SELECT id FROM");
97
98 if let Err(e) = execute!(
99 &conn,
100 &format!("DELETE FROM app_data WHERE id IN ({query_str})"),
101 params![&query.query.to_string()]
102 ) {
103 return Err(Error::MiscError(e.to_string()));
104 }
105
106 Ok(())
107 }
108
109 const MAXIMUM_FREE_APP_DATA: usize = 5;
110 const MAXIMUM_DATA_SIZE: usize = 205_000;
111
112 pub async fn create_app_data(&self, data: AppData) -> Result<AppData> {
117 let app = self.get_app_by_id(data.app).await?;
118
119 if data.key.len() < 1 {
121 return Err(Error::DataTooShort("key".to_string()));
122 } else if data.key.len() > 128 {
123 return Err(Error::DataTooLong("key".to_string()));
124 }
125
126 if data.value.len() < 1 {
127 return Err(Error::DataTooShort("value".to_string()));
128 } else if data.value.len() > Self::MAXIMUM_DATA_SIZE {
129 return Err(Error::DataTooLong("value".to_string()));
130 }
131
132 let owner = self.get_user_by_id(app.owner).await?;
134
135 if !owner.permissions.check(FinePermission::SUPPORTER) {
136 let app_data = self
137 .get_table_row_count_where("app_data", &format!("app = {}", data.app))
138 .await? as usize;
139
140 if app_data >= Self::MAXIMUM_FREE_APP_DATA {
141 return Err(Error::MiscError(
142 "You already have the maximum number of app_data you can have".to_string(),
143 ));
144 }
145 }
146
147 let conn = match self.0.connect().await {
149 Ok(c) => c,
150 Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
151 };
152
153 let res = execute!(
154 &conn,
155 "INSERT INTO app_data VALUES ($1, $2, $3, $4)",
156 params![
157 &(data.id as i64),
158 &(data.app as i64),
159 &data.key,
160 &data.value
161 ]
162 );
163
164 if let Err(e) = res {
165 return Err(Error::DatabaseError(e.to_string()));
166 }
167
168 Ok(data)
169 }
170
171 pub async fn delete_app_data(&self, id: usize) -> Result<()> {
172 let conn = match self.0.connect().await {
173 Ok(c) => c,
174 Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
175 };
176
177 let res = execute!(&conn, "DELETE FROM app_data WHERE id = $1", &[&(id as i64)]);
178
179 if let Err(e) = res {
180 return Err(Error::DatabaseError(e.to_string()));
181 }
182
183 self.0.1.remove(format!("atto.app_data:{}", id)).await;
184 Ok(())
185 }
186
187 auto_method!(update_app_data_key(&str) -> "UPDATE app_data SET k = $1 WHERE id = $2" --cache-key-tmpl="atto.app_data:{}");
188 auto_method!(update_app_data_value(&str) -> "UPDATE app_data SET v = $1 WHERE id = $2" --cache-key-tmpl="atto.app_data:{}");
189}