add: economy api

This commit is contained in:
trisua 2025-08-07 00:22:37 -04:00
parent 0a3ce3e9fe
commit 3c4ce1fae5
6 changed files with 51 additions and 16 deletions

View file

@ -250,7 +250,8 @@ svg.icon {
height: 1em;
}
svg.icon.filled {
svg.icon.filled,
.filled svg.icon {
fill: currentColor;
}

View file

@ -6,7 +6,6 @@
("class" "avatar shadow")
("loading" "lazy")
("style" "--size: {{ size }}"))
(text "{%- endmacro %} {% macro community_avatar(id, community=false, size=\"24px\") -%} {% if community -%}")
(img
("src" "/api/v1/communities/{{ id }}/avatar")
@ -14,7 +13,6 @@
("class" "avatar shadow")
("loading" "lazy")
("style" "--size: {{ size }}"))
(text "{% else %}")
(img
("src" "/api/v1/communities/{{ id }}/avatar")
@ -22,7 +20,6 @@
("class" "avatar shadow")
("loading" "lazy")
("style" "--size: {{ size }}"))
(text "{%- endif %} {%- endmacro %} {% macro banner(username, border_radius=\"var(--radius)\") -%}")
(img
("title" "{{ username }}'s banner")
@ -31,21 +28,18 @@
("class" "banner shadow w_full")
("loading" "lazy")
("style" "border-radius: {{ border_radius }};"))
(text "{%- endmacro %} {% macro community_banner(id, community=false) -%} {% if community %}")
(img
("src" "/api/v1/communities/{{ id }}/banner")
("alt" "{{ community.title }}'s banner")
("class" "banner shadow")
("loading" "lazy"))
(text "{% else %}")
(img
("src" "/api/v1/communities/{{ id }}/banner")
("alt" "{{ id }}'s banner")
("class" "banner shadow")
("loading" "lazy"))
(text "{%- endif %} {%- endmacro %} {% macro community_listing_card(community) -%}")
(a
("class" "card secondary w_full flex items_center gap_4")
@ -69,12 +63,10 @@
(b
(text "{{ community.member_count }} "))
(text "members"))))
(text "{%- endmacro %} {% macro username(user) -%}")
(div
("style" "display: contents")
(text "{% if user.settings.display_name -%} {{ user.settings.display_name }} {% else %} {{ user.username }} {%- endif %}"))
(text "{%- endmacro %} {% macro likes(id, asset_type, likes=0, dislikes=0, secondary=false, disable_dislikes=false) -%}")
(button
("title" "Like")
@ -85,7 +77,6 @@
(span
(text "{{ likes }}"))
(text "{%- endif %}"))
(text "{% if not user or not user.settings.hide_dislikes and not disable_dislikes -%}")
(button
("title" "Dislike")
@ -96,15 +87,21 @@
(span
(text "{{ dislikes }}"))
(text "{%- endif %}"))
(text "{%- endif %} {%- endmacro %} {% macro full_username(user) -%} {% if user and user.username -%}")
(div
("class" "flex items_center")
(a
("href" "/@{{ user.username }}")
("class" "flush")
("class" "flush flex gap_1")
("style" "font-weight: 600")
("target" "_top")
(text "{% if user.settings.private_profile -%}")
(span
("title" "Private")
("class" "flex items_center")
(icon (text "lock")))
(text "{%- endif %}")
(text "{% if user.permissions|has_banned -%}")
(del ("class" "fade") (text "{{ self::username(user=user) }}"))
(text "{% else %}")
@ -134,7 +131,6 @@
("style" "display: contents")
(text "{{ self::post(post=post, owner=owner, secondary=secondary, community=community, show_community=show_community, can_manage_post=can_manage_post, repost=repost, expect_repost=true) }}"))
(text "{%- endmacro %}")
(text "{% macro post_info(post, community) -%}")
; info about the post: edited, date, etc.
(text "{% if post.context.edited != 0 -%}")
@ -200,7 +196,6 @@
(text "{{ icon \"trash-2\" }}"))
(text "{%- endif %}")
(text "{%- endmacro %}")
(text "{% macro post_buttons_box(post, community, owner, can_manage_post, show_comments=true) -%}")
(div
("class" "flex justify_between items_center gap_2 w_full")

View file

@ -0,0 +1,28 @@
use crate::{get_user_from_token, State, cookie::CookieJar};
use axum::{response::IntoResponse, Extension, Json};
use tetratto_core::model::{economy::CoinTransfer, oauth, ApiReturn, Error};
use super::CreateCoinTransfer;
pub async fn create_request(
jar: CookieJar,
Extension(data): Extension<State>,
Json(req): Json<CreateCoinTransfer>,
) -> impl IntoResponse {
let data = &(data.read().await).0;
let user = match get_user_from_token!(jar, data, oauth::AppScope::UserSendCoins) {
Some(ua) => ua,
None => return Json(Error::NotAllowed.into()),
};
match data
.create_transfer(CoinTransfer::new(user.id, req.receiver, req.amount))
.await
{
Ok(s) => Json(ApiReturn {
ok: true,
message: "Stack created".to_string(),
payload: s.id.to_string(),
}),
Err(e) => Json(e.into()),
}
}

View file

@ -4,6 +4,7 @@ pub mod auth;
pub mod channels;
pub mod communities;
pub mod domains;
pub mod economy;
pub mod journals;
pub mod letters;
pub mod notes;
@ -705,6 +706,8 @@ pub fn routes() -> Router {
.route("/letters/{id}/read", post(letters::add_read_request))
.route("/letters/sent", get(letters::list_sent_request))
.route("/letters/received", get(letters::list_received_request))
// economy
.route("/transfers", post(economy::create_request))
}
pub fn lw_routes() -> Router {
@ -1212,3 +1215,9 @@ pub struct CreateLetter {
pub content: String,
pub replying_to: String,
}
#[derive(Deserialize)]
pub struct CreateCoinTransfer {
pub receiver: usize,
pub amount: i32,
}

View file

@ -4,7 +4,7 @@ use crate::{auto_method, DataManager};
use oiseau::{cache::Cache, execute, get, params, query_rows, PostgresRow};
impl DataManager {
/// Get a [`Letter`] from an SQL row.
/// Get a [`CoinTransfer`] from an SQL row.
pub(crate) fn get_transfer_from_row(x: &PostgresRow) -> CoinTransfer {
CoinTransfer {
id: get!(x->0(i64)) as usize,
@ -51,7 +51,7 @@ impl DataManager {
/// Create a new transfer in the database.
///
/// # Arguments
/// * `data` - a mock [`Letter`] object to insert
/// * `data` - a mock [`CoinTransfer`] object to insert
pub async fn create_transfer(&self, data: CoinTransfer) -> Result<CoinTransfer> {
// check values
let mut sender = self.get_user_by_id(data.sender).await?;

View file

@ -106,6 +106,8 @@ pub enum AppScope {
UserCreateProducts,
/// Create letters on behalf of the user.
UserCreateLetters,
/// Send coins on behalf of the user.
UserSendCoins,
/// Delete posts owned by the user.
UserDeletePosts,
/// Delete messages owned by the user.