195 lines
5.2 KiB
Rust
195 lines
5.2 KiB
Rust
use crate::{
|
|
cookie::CookieJar,
|
|
get_user_from_token,
|
|
image::{save_webp_buffer, JsonMultipart},
|
|
State,
|
|
};
|
|
use axum::{
|
|
extract::Path,
|
|
response::{Html, IntoResponse},
|
|
Extension, Json,
|
|
http::StatusCode,
|
|
};
|
|
use tetratto_core::model::{
|
|
economy::UserAd,
|
|
oauth,
|
|
uploads::{MediaType, MediaUpload},
|
|
ApiReturn, Error,
|
|
};
|
|
use super::{CreateAd, UpdateAdIsRunning};
|
|
|
|
const MAXIMUM_AD_FILE_SIZE: usize = 2_097_152;
|
|
|
|
pub async fn create_request(
|
|
jar: CookieJar,
|
|
Extension(data): Extension<State>,
|
|
JsonMultipart(bytes_parts, req): JsonMultipart<CreateAd>,
|
|
) -> impl IntoResponse {
|
|
let data = &(data.read().await).0;
|
|
let user = match get_user_from_token!(jar, data, oauth::AppScope::UserCreateProducts) {
|
|
Some(ua) => ua,
|
|
None => return Json(Error::NotAllowed.into()),
|
|
};
|
|
|
|
// get file
|
|
let file = match bytes_parts.get(0) {
|
|
Some(x) => x,
|
|
None => return Json(Error::Unknown.into()),
|
|
};
|
|
|
|
if file.len() > MAXIMUM_AD_FILE_SIZE {
|
|
return Json(Error::FileTooLarge.into());
|
|
}
|
|
|
|
let upload = match data
|
|
.create_upload(MediaUpload::new(MediaType::Webp, user.id))
|
|
.await
|
|
{
|
|
Ok(x) => x,
|
|
Err(e) => return Json(e.into()),
|
|
};
|
|
|
|
match data
|
|
.create_ad(UserAd::new(user.id, upload.id, req.target, req.size))
|
|
.await
|
|
{
|
|
Ok(_) => {
|
|
// write image
|
|
if let Err(e) =
|
|
save_webp_buffer(&upload.path(&data.0.0).to_string(), file.to_vec(), None)
|
|
{
|
|
return Json(Error::MiscError(e.to_string()).into());
|
|
}
|
|
|
|
// ...
|
|
Json(ApiReturn {
|
|
ok: true,
|
|
message: "Ad created".to_string(),
|
|
payload: (),
|
|
})
|
|
}
|
|
Err(e) => Json(e.into()),
|
|
}
|
|
}
|
|
|
|
pub async fn delete_request(
|
|
jar: CookieJar,
|
|
Extension(data): Extension<State>,
|
|
Path(id): Path<usize>,
|
|
) -> impl IntoResponse {
|
|
let data = &(data.read().await).0;
|
|
let user = match get_user_from_token!(jar, data, oauth::AppScope::UserManageProducts) {
|
|
Some(ua) => ua,
|
|
None => return Json(Error::NotAllowed.into()),
|
|
};
|
|
|
|
match data.delete_ad(id, &user).await {
|
|
Ok(_) => Json(ApiReturn {
|
|
ok: true,
|
|
message: "Ad deleted".to_string(),
|
|
payload: (),
|
|
}),
|
|
Err(e) => Json(e.into()),
|
|
}
|
|
}
|
|
|
|
pub async fn update_is_running_request(
|
|
jar: CookieJar,
|
|
Extension(data): Extension<State>,
|
|
Path(id): Path<usize>,
|
|
Json(req): Json<UpdateAdIsRunning>,
|
|
) -> impl IntoResponse {
|
|
let data = &(data.read().await).0;
|
|
let user = match get_user_from_token!(jar, data, oauth::AppScope::UserManageProducts) {
|
|
Some(ua) => ua,
|
|
None => return Json(Error::NotAllowed.into()),
|
|
};
|
|
|
|
let ad = match data.get_ad_by_id(id).await {
|
|
Ok(x) => x,
|
|
Err(e) => return Json(e.into()),
|
|
};
|
|
|
|
if !ad.is_running && user.coins < 50 {
|
|
return Json(
|
|
Error::MiscError(
|
|
"You must have a minimum of 50 coins in your balance to run ads".to_string(),
|
|
)
|
|
.into(),
|
|
);
|
|
}
|
|
|
|
match data
|
|
.update_ad_is_running(id, &user, if req.is_running { 1 } else { 0 })
|
|
.await
|
|
{
|
|
Ok(_) => Json(ApiReturn {
|
|
ok: true,
|
|
message: "Ad updated".to_string(),
|
|
payload: (),
|
|
}),
|
|
Err(e) => Json(e.into()),
|
|
}
|
|
}
|
|
|
|
pub async fn click_request(
|
|
jar: CookieJar,
|
|
Extension(data): Extension<State>,
|
|
Path((host, id)): Path<(usize, usize)>,
|
|
) -> impl IntoResponse {
|
|
let data = &(data.read().await).0;
|
|
let user = get_user_from_token!(jar, data);
|
|
|
|
let bad_return = (
|
|
StatusCode::FOUND,
|
|
[
|
|
(
|
|
"Set-Cookie".to_string(),
|
|
format!(
|
|
"Atto-Clicked=true; Path=/api/v1/ads/host/{host}/{id}/click; Max-Age=86400"
|
|
),
|
|
),
|
|
("Location".to_string(), data.0.0.host.clone()),
|
|
],
|
|
Html(""),
|
|
);
|
|
|
|
if jar.get("Atto-Clicked").is_some() {
|
|
// we've already clicked this ad, don't charge
|
|
match data.get_ad_by_id(id).await {
|
|
Ok(x) => {
|
|
return (
|
|
StatusCode::FOUND,
|
|
[
|
|
(
|
|
"Set-Cookie".to_string(),
|
|
format!(
|
|
"Atto-Clicked=true; Path=/api/v1/ads/host/{host}/{id}/click; Max-Age=86400"
|
|
),
|
|
),
|
|
("Location".to_string(), x.target),
|
|
],
|
|
Html(""),
|
|
);
|
|
}
|
|
Err(_) => return bad_return,
|
|
}
|
|
}
|
|
|
|
match data.ad_click(host, id, user).await {
|
|
Ok(t) => (
|
|
StatusCode::FOUND,
|
|
[
|
|
(
|
|
"Set-Cookie".to_string(),
|
|
format!(
|
|
"Atto-Clicked=true; Path=/api/v1/ads/host/{host}/{id}/click; Max-Age=86400"
|
|
),
|
|
),
|
|
("Location".to_string(), t),
|
|
],
|
|
Html(""),
|
|
),
|
|
Err(_) => bad_return,
|
|
}
|
|
}
|