authbeam/api/
items.rs

1use crate::database::Database;
2use crate::model::{
3    DatabaseError, ItemCreate, ItemEdit, ItemEditContent, ItemType, SetItemStatus, TokenPermission,
4    TransactionCreate,
5};
6use databeam::prelude::DefaultReturn;
7
8use axum::response::IntoResponse;
9use axum::{
10    extract::{Path, State},
11    Json,
12};
13use axum_extra::extract::cookie::CookieJar;
14
15/// Get an item
16pub async fn get_request(
17    State(database): State<Database>,
18    Path(id): Path<String>,
19) -> impl IntoResponse {
20    // get item
21    let item = match database.get_item(&id).await {
22        Ok(i) => i,
23        Err(e) => return Json(e.to_json()),
24    };
25
26    // we can only view the content of modules through the api :)
27    if item.r#type != ItemType::Module {
28        return Json(DatabaseError::NotAllowed.to_json());
29    }
30
31    // return
32    Json(DefaultReturn {
33        success: true,
34        message: item.id.to_string(),
35        payload: Some(item),
36    })
37}
38
39/// Create an item
40pub async fn create_request(
41    jar: CookieJar,
42    State(database): State<Database>,
43    Json(props): Json<ItemCreate>,
44) -> impl IntoResponse {
45    // get user from token
46    let auth_user = match jar.get("__Secure-Token") {
47        Some(c) => {
48            let token = c.value_trimmed();
49            match database.get_profile_by_unhashed(token).await {
50                Ok(ua) => {
51                    // check token permission
52                    if !ua
53                        .token_context_from_token(&token)
54                        .can_do(TokenPermission::ManageAssets)
55                    {
56                        return Json(DatabaseError::NotAllowed.to_json());
57                    }
58
59                    // return
60                    ua
61                }
62                Err(e) => return Json(e.to_json()),
63            }
64        }
65        None => return Json(DatabaseError::NotAllowed.to_json()),
66    };
67
68    // return
69    let item = match database.create_item(props, &auth_user.id).await {
70        Ok(m) => m,
71        Err(e) => return Json(e.to_json()),
72    };
73
74    Json(DefaultReturn {
75        success: true,
76        message: "Item created".to_string(),
77        payload: Some(item),
78    })
79}
80
81/// Delete an item
82pub async fn delete_request(
83    jar: CookieJar,
84    Path(id): Path<String>,
85    State(database): State<Database>,
86) -> impl IntoResponse {
87    // get user from token
88    let auth_user = match jar.get("__Secure-Token") {
89        Some(c) => match database.get_profile_by_unhashed(c.value_trimmed()).await {
90            Ok(ua) => ua,
91            Err(e) => return Json(e.to_json()),
92        },
93        None => return Json(DatabaseError::NotAllowed.to_json()),
94    };
95
96    // return
97    if let Err(e) = database.delete_item(&id, auth_user).await {
98        return Json(e.to_json());
99    }
100
101    Json(DefaultReturn {
102        success: true,
103        message: "Item deleted".to_string(),
104        payload: (),
105    })
106}
107
108/// Update item status
109pub async fn update_status_request(
110    jar: CookieJar,
111    Path(id): Path<String>,
112    State(database): State<Database>,
113    Json(props): Json<SetItemStatus>,
114) -> impl IntoResponse {
115    // get user from token
116    let auth_user = match jar.get("__Secure-Token") {
117        Some(c) => match database.get_profile_by_unhashed(c.value_trimmed()).await {
118            Ok(ua) => ua,
119            Err(e) => return Json(e.to_json()),
120        },
121        None => return Json(DatabaseError::NotAllowed.to_json()),
122    };
123
124    // return
125    if let Err(e) = database
126        .update_item_status(&id, props.status, auth_user)
127        .await
128    {
129        return Json(e.to_json());
130    }
131
132    Json(DefaultReturn {
133        success: true,
134        message: "Item updated".to_string(),
135        payload: (),
136    })
137}
138
139/// Update item fields
140pub async fn update_item_request(
141    jar: CookieJar,
142    Path(id): Path<String>,
143    State(database): State<Database>,
144    Json(props): Json<ItemEdit>,
145) -> impl IntoResponse {
146    // get user from token
147    let auth_user = match jar.get("__Secure-Token") {
148        Some(c) => match database.get_profile_by_unhashed(c.value_trimmed()).await {
149            Ok(ua) => ua,
150            Err(e) => return Json(e.to_json()),
151        },
152        None => return Json(DatabaseError::NotAllowed.to_json()),
153    };
154
155    // return
156    if let Err(e) = database.update_item(&id, props, auth_user).await {
157        return Json(e.to_json());
158    }
159
160    Json(DefaultReturn {
161        success: true,
162        message: "Item updated".to_string(),
163        payload: (),
164    })
165}
166
167/// Update item content
168pub async fn update_item_content_request(
169    jar: CookieJar,
170    Path(id): Path<String>,
171    State(database): State<Database>,
172    Json(props): Json<ItemEditContent>,
173) -> impl IntoResponse {
174    // get user from token
175    let auth_user = match jar.get("__Secure-Token") {
176        Some(c) => match database.get_profile_by_unhashed(c.value_trimmed()).await {
177            Ok(ua) => ua,
178            Err(e) => return Json(e.to_json()),
179        },
180        None => return Json(DatabaseError::NotAllowed.to_json()),
181    };
182
183    // return
184    if let Err(e) = database.update_item_content(&id, props, auth_user).await {
185        return Json(e.to_json());
186    }
187
188    Json(DefaultReturn {
189        success: true,
190        message: "Item updated".to_string(),
191        payload: (),
192    })
193}
194
195/// Buy an item
196pub async fn buy_request(
197    jar: CookieJar,
198    Path(id): Path<String>,
199    State(database): State<Database>,
200) -> impl IntoResponse {
201    // get user from token
202    let auth_user = match jar.get("__Secure-Token") {
203        Some(c) => match database.get_profile_by_unhashed(c.value_trimmed()).await {
204            Ok(ua) => ua,
205            Err(e) => return Json(e.to_json()),
206        },
207        None => return Json(DatabaseError::NotAllowed.to_json()),
208    };
209
210    // return
211    let item = match database.get_item(&id).await {
212        Ok(i) => i,
213        Err(e) => return Json(e.to_json()),
214    };
215
216    if item.cost == -1 {
217        return Json(DefaultReturn {
218            success: false,
219            message: DatabaseError::TooExpensive.to_string(),
220            payload: (),
221        });
222    }
223
224    // make sure we don't already have this item
225    if let Ok(_) = database
226        .get_transaction_by_customer_item(&auth_user.id, &item.id)
227        .await
228    {
229        return Json(DefaultReturn {
230            success: false,
231            message: DatabaseError::MustBeUnique.to_string(),
232            payload: (),
233        });
234    }
235
236    // ...
237    if let Err(e) = database
238        .create_transaction(
239            TransactionCreate {
240                merchant: item.creator.clone(),
241                item: item.id.clone(),
242                amount: -(item.cost),
243            },
244            &auth_user.id,
245        )
246        .await
247    {
248        return Json(e.to_json());
249    }
250
251    Json(DefaultReturn {
252        success: true,
253        message: "Purchase successful".to_string(),
254        payload: (),
255    })
256}