1use authbeam::layout::LayoutComponent;
2use reva_axum::Template;
3use axum::response::IntoResponse;
4use axum::{
5 extract::{State, Query, Path},
6 response::Html,
7};
8use axum_extra::extract::CookieJar;
9
10use authbeam::model::{FinePermission, Item, ItemStatus, ItemType, Profile};
11
12use crate::config::Config;
13use crate::database::Database;
14use crate::model::DatabaseError;
15use crate::ToHtml;
16
17use super::MarketQuery;
18
19#[derive(Template)]
20#[template(path = "market/homepage.html")]
21struct HomepageTemplate {
22 config: Config,
23 lang: langbeam::LangFile,
24 profile: Option<Box<Profile>>,
25 unread: usize,
26 notifs: usize,
27 page: i32,
28 query: String,
29 status: ItemStatus,
30 creator: String,
31 customer: String,
32 items: Vec<(Item, Box<Profile>)>,
33 is_helper: bool,
34}
35
36pub async fn homepage_request(
38 jar: CookieJar,
39 State(database): State<Database>,
40 Query(props): Query<MarketQuery>,
41) -> impl IntoResponse {
42 let auth_user = match jar.get("__Secure-Token") {
43 Some(c) => match database
44 .auth
45 .get_profile_by_unhashed(c.value_trimmed())
46 .await
47 {
48 Ok(ua) => ua,
49 Err(_) => return Html(DatabaseError::NotAllowed.to_html(database)),
50 },
51 None => return Html(DatabaseError::NotAllowed.to_html(database)),
52 };
53
54 let unread = database.get_inbox_count_by_recipient(&auth_user.id).await;
55
56 let notifs = database
57 .auth
58 .get_notification_count_by_recipient(&auth_user.id)
59 .await;
60
61 let group = match database.auth.get_group_by_id(auth_user.group).await {
63 Ok(g) => g,
64 Err(e) => return Html(e.to_string()),
65 };
66
67 if (props.status != ItemStatus::Approved) && (props.status != ItemStatus::Featured) {
68 if !group.permissions.check(FinePermission::ECON_MASTER) {
70 return Html(DatabaseError::NotAllowed.to_string());
72 }
73 }
74
75 let is_helper = group.permissions.check_helper();
76
77 if !props.customer.is_empty() && (props.customer != auth_user.id) && !is_helper {
78 return Html(DatabaseError::NotAllowed.to_html(database));
80 }
81
82 let items = if props.creator.is_empty() {
84 if let Some(r#type) = props.r#type {
85 match database
86 .auth
87 .get_items_by_type_paginated(r#type, props.page)
88 .await
89 {
90 Ok(i) => i,
91 Err(e) => return Html(e.to_string()),
92 }
93 } else {
94 match database
95 .auth
96 .get_items_by_status_searched_paginated(props.status.clone(), props.page, &props.q)
97 .await
98 {
99 Ok(i) => i,
100 Err(e) => return Html(e.to_string()),
101 }
102 }
103 } else if !props.customer.is_empty() {
104 match database
105 .auth
106 .get_transactions_by_customer_paginated(&props.customer, props.page)
107 .await
108 {
109 Ok(i) => {
110 let mut out = Vec::new();
111
112 for x in i {
113 out.push((
114 match x.0 .1 {
115 Some(i) => i.clone(),
116 None => return Html(DatabaseError::NotFound.to_html(database)),
117 },
118 x.2.clone(),
119 ))
120 }
121
122 out
123 }
124 Err(e) => return Html(e.to_string()),
125 }
126 } else {
127 if (auth_user.id != props.creator) && !is_helper {
128 return Html(DatabaseError::NotAllowed.to_html(database));
130 }
131
132 if let Some(r#type) = props.r#type {
133 match database
135 .auth
136 .get_items_by_creator_type_paginated(&props.creator, r#type, props.page)
137 .await
138 {
139 Ok(i) => i,
140 Err(e) => return Html(e.to_string()),
141 }
142 } else {
143 match database
145 .auth
146 .get_items_by_creator_paginated(&props.creator, props.page)
147 .await
148 {
149 Ok(i) => i,
150 Err(e) => return Html(e.to_string()),
151 }
152 }
153 };
154
155 Html(
157 HomepageTemplate {
158 config: database.config.clone(),
159 lang: database.lang(if let Some(c) = jar.get("net.rainbeam.langs.choice") {
160 c.value_trimmed()
161 } else {
162 ""
163 }),
164 profile: Some(auth_user),
165 unread,
166 notifs,
167 page: props.page,
168 query: props.q,
169 status: props.status,
170 creator: props.creator,
171 customer: props.customer,
172 items,
173 is_helper,
174 }
175 .render()
176 .unwrap(),
177 )
178}
179
180#[derive(Template)]
181#[template(path = "market/new.html")]
182struct CreateTemplate {
183 config: Config,
184 lang: langbeam::LangFile,
185 profile: Option<Box<Profile>>,
186 unread: usize,
187 notifs: usize,
188}
189
190pub async fn create_request(jar: CookieJar, State(database): State<Database>) -> impl IntoResponse {
192 let auth_user = match jar.get("__Secure-Token") {
193 Some(c) => match database
194 .auth
195 .get_profile_by_unhashed(c.value_trimmed())
196 .await
197 {
198 Ok(ua) => ua,
199 Err(_) => return Html(DatabaseError::NotAllowed.to_html(database)),
200 },
201 None => return Html(DatabaseError::NotAllowed.to_html(database)),
202 };
203
204 let unread = database.get_inbox_count_by_recipient(&auth_user.id).await;
205
206 let notifs = database
207 .auth
208 .get_notification_count_by_recipient(&auth_user.id)
209 .await;
210
211 Html(
212 CreateTemplate {
213 config: database.config.clone(),
214 lang: database.lang(if let Some(c) = jar.get("net.rainbeam.langs.choice") {
215 c.value_trimmed()
216 } else {
217 ""
218 }),
219 profile: Some(auth_user),
220 unread,
221 notifs,
222 }
223 .render()
224 .unwrap(),
225 )
226}
227
228#[derive(Template)]
229#[template(path = "market/item.html")]
230struct ItemTemplate {
231 config: Config,
232 lang: langbeam::LangFile,
233 profile: Option<Box<Profile>>,
234 unread: usize,
235 notifs: usize,
236 item: Item,
237 creator: Box<Profile>,
238 is_owned: bool,
239 is_helper: bool,
240 reaction_count: usize,
241}
242
243pub async fn item_request(
245 jar: CookieJar,
246 Path(id): Path<String>,
247 State(database): State<Database>,
248) -> impl IntoResponse {
249 let auth_user = match jar.get("__Secure-Token") {
250 Some(c) => match database
251 .auth
252 .get_profile_by_unhashed(c.value_trimmed())
253 .await
254 {
255 Ok(ua) => ua,
256 Err(_) => return Html(DatabaseError::NotAllowed.to_html(database)),
257 },
258 None => return Html(DatabaseError::NotAllowed.to_html(database)),
259 };
260
261 let unread = database.get_inbox_count_by_recipient(&auth_user.id).await;
262
263 let notifs = database
264 .auth
265 .get_notification_count_by_recipient(&auth_user.id)
266 .await;
267
268 let group = match database.auth.get_group_by_id(auth_user.group).await {
270 Ok(g) => g,
271 Err(e) => return Html(e.to_string()),
272 };
273
274 let is_helper = group.permissions.check_helper();
275
276 let item = match database.auth.get_item(&id).await {
278 Ok(i) => i,
279 Err(e) => return Html(e.to_string()),
280 };
281
282 if !is_helper
283 && (item.status != ItemStatus::Approved)
284 && (item.status != ItemStatus::Featured)
285 && auth_user.id != item.creator
286 {
287 return Html(DatabaseError::NotAllowed.to_string());
289 }
290
291 Html(
293 ItemTemplate {
294 config: database.config.clone(),
295 lang: database.lang(if let Some(c) = jar.get("net.rainbeam.langs.choice") {
296 c.value_trimmed()
297 } else {
298 ""
299 }),
300 is_owned: database
301 .auth
302 .get_transaction_by_customer_item(&auth_user.id, &item.id)
303 .await
304 .is_ok(),
305 profile: Some(auth_user),
306 unread,
307 notifs,
308 creator: match database.auth.get_profile(&item.creator).await {
309 Ok(ua) => ua,
310 Err(e) => return Html(e.to_string()),
311 },
312 item,
313 is_helper,
314 reaction_count: database.get_reaction_count_by_asset(id).await,
315 }
316 .render()
317 .unwrap(),
318 )
319}
320
321#[derive(Template)]
322#[template(path = "partials/components/theme_playground.html")]
323struct ThemePlaygroundTemplate {
324 config: Config,
325 lang: langbeam::LangFile,
326 profile: Option<Box<Profile>>,
327 css: String,
328}
329
330pub async fn theme_playground_request(
332 jar: CookieJar,
333 Path(id): Path<String>,
334 State(database): State<Database>,
335) -> impl IntoResponse {
336 let auth_user = match jar.get("__Secure-Token") {
337 Some(c) => match database
338 .auth
339 .get_profile_by_unhashed(c.value_trimmed())
340 .await
341 {
342 Ok(ua) => ua,
343 Err(_) => return Html(DatabaseError::NotAllowed.to_html(database)),
344 },
345 None => return Html(DatabaseError::NotAllowed.to_html(database)),
346 };
347
348 let item = match database.auth.get_item(&id).await {
350 Ok(i) => i,
351 Err(e) => return Html(e.to_string()),
352 };
353
354 if item.r#type != ItemType::UserTheme {
355 return Html(DatabaseError::ValueError.to_string());
356 }
357
358 Html(
360 ThemePlaygroundTemplate {
361 config: database.config.clone(),
362 lang: database.lang(if let Some(c) = jar.get("net.rainbeam.langs.choice") {
363 c.value_trimmed()
364 } else {
365 ""
366 }),
367 profile: Some(auth_user),
368 css: item.content,
369 }
370 .render()
371 .unwrap(),
372 )
373}
374
375#[derive(Template)]
376#[template(path = "partials/components/layout_playground.html")]
377struct LayoutPlaygroundTemplate {
378 config: Config,
379 lang: langbeam::LangFile,
380 profile: Option<Box<Profile>>,
381 layout: LayoutComponent,
382}
383
384pub async fn layout_playground_request(
386 jar: CookieJar,
387 Path(id): Path<String>,
388 State(database): State<Database>,
389) -> impl IntoResponse {
390 let auth_user = match jar.get("__Secure-Token") {
391 Some(c) => match database
392 .auth
393 .get_profile_by_unhashed(c.value_trimmed())
394 .await
395 {
396 Ok(ua) => ua,
397 Err(_) => return Html(DatabaseError::NotAllowed.to_html(database)),
398 },
399 None => return Html(DatabaseError::NotAllowed.to_html(database)),
400 };
401
402 let item = match database.auth.get_item(&id).await {
404 Ok(i) => i,
405 Err(e) => return Html(e.to_string()),
406 };
407
408 if item.r#type != ItemType::Layout {
409 return Html(DatabaseError::ValueError.to_string());
410 }
411
412 Html(
414 LayoutPlaygroundTemplate {
415 config: database.config.clone(),
416 lang: database.lang(if let Some(c) = jar.get("net.rainbeam.langs.choice") {
417 c.value_trimmed()
418 } else {
419 ""
420 }),
421 profile: Some(auth_user),
422 layout: match serde_json::from_str(&item.content) {
423 Ok(l) => l,
424 Err(_) => return Html(DatabaseError::ValueError.to_string()),
425 },
426 }
427 .render()
428 .unwrap(),
429 )
430}