add more market and vendor api support

This commit is contained in:
creating2morrow 2023-06-03 01:13:56 -04:00
parent 660aa3c9bd
commit 8cf7337e8c
11 changed files with 198 additions and 131 deletions

View file

@ -16,6 +16,9 @@ use log::{
use rocket::serde::json::Json;
use std::error::Error;
/// Environment variable for activating vendor functionality
pub const NEVMES_VENDOR_ENABLED: &str = "NEVMES_VENDOR_ENABLED";
/// Create a new contact
pub async fn create(c: &Json<Contact>) -> Contact {
let f_cid: String = format!("c{}", utils::generate_rnd());
@ -24,6 +27,7 @@ pub async fn create(c: &Json<Contact>) -> Contact {
cid: String::from(&f_cid),
gpg_key: c.gpg_key.iter().cloned().collect(),
i2p_address: String::from(&c.i2p_address),
is_vendor: false,
xmr_address: String::from(&c.xmr_address),
};
let is_valid = validate_contact(c).await;
@ -95,6 +99,8 @@ async fn validate_contact(j: &Json<Contact>) -> bool {
/// Send our information
pub async fn share() -> Contact {
let vendor_env = std::env::var(NEVMES_VENDOR_ENABLED).unwrap_or(String::from("0"));
let is_vendor = vendor_env == String::from("1");
let m_address: reqres::XmrRpcAddressResponse = monero::get_address().await;
let gpg_key = gpg::export_key().unwrap_or(Vec::new());
let i2p_address = i2p::get_destination();
@ -103,6 +109,7 @@ pub async fn share() -> Contact {
cid: utils::empty_string(),
gpg_key,
i2p_address,
is_vendor,
xmr_address,
}
}

View file

@ -5,8 +5,9 @@ use crate::{
gpg,
i2p,
models::*,
monero,
reqres,
utils, monero,
utils,
};
use log::{
debug,
@ -34,6 +35,13 @@ struct MultisigMessageData {
orid: String,
}
/*
TODOs(c2m):
- add wallet open before multisig methods
- API to valid payment and import multisig info
- API to sign and submit the signed tx set
*/
/// Create a new message
pub async fn create(m: Json<Message>, jwp: String, m_type: MessageType) -> Message {
let rnd = utils::generate_rnd();
@ -121,7 +129,11 @@ fn parse_multisig_message(mid: String) -> MultisigMessageData {
let info: String = v.remove(0);
bytes = Vec::new();
debug!("zero decryption bytes: {:?}", bytes);
MultisigMessageData { info, sub_type, orid }
MultisigMessageData {
info,
sub_type,
orid,
}
}
/// Rx multisig message
@ -166,7 +178,10 @@ pub async fn rx_multisig(m: Json<Message>) {
let k = &new_message.mid;
db::Interface::async_write(&s.env, &s.handle, k, &Message::to_db(&new_message)).await;
let data: MultisigMessageData = parse_multisig_message(new_message.mid);
debug!("writing multisig message type {} for order {}", &data.sub_type, &data.orid);
debug!(
"writing multisig message type {} for order {}",
&data.sub_type, &data.orid
);
// lookup msig message data by {type}-{order id}-{contact .b32.i2p address}
let msig_key = format!("{}-{}-{}", &data.sub_type, &data.orid, &m.from);
db::Interface::async_write(&s.env, &s.handle, &msig_key, &data.info).await;
@ -439,14 +454,15 @@ pub async fn send_prepare_info(orid: &String, contact: &String) {
let prepare_info = monero::prepare_wallet().await;
let k = format!("{}-{}", "fts-jwp", contact);
let jwp = db::Interface::read(&s.env, &s.handle, &k);
let body_str = format!("{}:{}:{}", PREPARE_MSIG, orid, &prepare_info.result.multisig_info);
let body_str = format!(
"{}:{}:{}",
PREPARE_MSIG, orid, &prepare_info.result.multisig_info
);
let message: Message = Message {
mid: utils::empty_string(),
uid: utils::empty_string(),
body: body_str.into_bytes(),
created: chrono::Utc::now().timestamp(),
from: utils::empty_string(),
to: String::from(contact),
..Default::default()
};
let j_message: Json<Message> = utils::message_to_json(&message);
create(j_message, jwp, MessageType::Multisig).await;
@ -462,12 +478,10 @@ pub async fn send_make_info(orid: &String, contact: &String, info: Vec<String>)
let jwp = db::Interface::read(&s.env, &s.handle, &k);
let body_str = format!("{}:{}:{}", MAKE_MSIG, orid, &make_info.result.multisig_info);
let message: Message = Message {
mid: utils::empty_string(),
uid: utils::empty_string(),
body: body_str.into_bytes(),
created: chrono::Utc::now().timestamp(),
from: utils::empty_string(),
to: String::from(contact),
..Default::default()
};
let j_message: Json<Message> = utils::message_to_json(&message);
create(j_message, jwp, MessageType::Multisig).await;
@ -483,14 +497,15 @@ pub async fn send_exchange_info(orid: &String, contact: &String, info: Vec<Strin
let exchange_info = monero::exchange_multisig_keys(false, info, wallet_password).await;
let k = format!("{}-{}", "fts-jwp", contact);
let jwp = db::Interface::read(&s.env, &s.handle, &k);
let body_str = format!("{}:{}:{}", EXCHANGE_MSIG, orid, &exchange_info.result.multisig_info);
let body_str = format!(
"{}:{}:{}",
EXCHANGE_MSIG, orid, &exchange_info.result.multisig_info
);
let message: Message = Message {
mid: utils::empty_string(),
uid: utils::empty_string(),
body: body_str.into_bytes(),
created: chrono::Utc::now().timestamp(),
from: utils::empty_string(),
to: String::from(contact),
..Default::default()
};
let j_message: Json<Message> = utils::message_to_json(&message);
create(j_message, jwp, MessageType::Multisig).await;
@ -506,12 +521,10 @@ pub async fn send_export_info(orid: &String, contact: &String) {
let jwp = db::Interface::read(&s.env, &s.handle, &k);
let body_str = format!("{}:{}:{}", EXPORT_MSIG, orid, &exchange_info.result.info);
let message: Message = Message {
mid: utils::empty_string(),
uid: utils::empty_string(),
body: body_str.into_bytes(),
created: chrono::Utc::now().timestamp(),
from: utils::empty_string(),
to: String::from(contact),
..Default::default()
};
let j_message: Json<Message> = utils::message_to_json(&message);
create(j_message, jwp, MessageType::Multisig).await;

View file

@ -1,5 +1,6 @@
use crate::utils;
use rocket::serde::{
json::Json,
Deserialize,
Serialize,
};
@ -88,6 +89,7 @@ impl Authorization {
pub struct Contact {
pub cid: String,
pub i2p_address: String,
pub is_vendor: bool,
pub xmr_address: String,
pub gpg_key: Vec<u8>,
}
@ -98,6 +100,7 @@ impl Default for Contact {
cid: utils::empty_string(),
gpg_key: Vec::new(),
i2p_address: utils::empty_string(),
is_vendor: false,
xmr_address: utils::empty_string(),
}
}
@ -106,18 +109,26 @@ impl Default for Contact {
impl Contact {
pub fn to_db(c: &Contact) -> String {
let gpg = hex::encode(&c.gpg_key);
format!("{}!{}!{}", gpg, c.i2p_address, c.xmr_address)
format!(
"{}!{}!{}!{}",
gpg, c.i2p_address, c.is_vendor, c.xmr_address
)
}
pub fn from_db(k: String, v: String) -> Contact {
let values = v.split("!");
let mut v: Vec<String> = values.map(|s| String::from(s)).collect();
let gpg_key = hex::decode(v.remove(0)).unwrap_or(Vec::new());
let i2p_address = v.remove(0);
let is_vendor = match v.remove(0).parse::<bool>() {
Ok(n) => n,
Err(_e) => false,
};
let xmr_address = v.remove(0);
Contact {
cid: k,
gpg_key,
i2p_address,
is_vendor,
xmr_address,
}
}
@ -279,23 +290,15 @@ impl Product {
qty,
}
}
pub fn update(
p: Product,
description: String,
image: Vec<u8>,
in_stock: bool,
name: String,
price: i64,
qty: i64,
) -> Product {
pub fn update(p: Product, jp: &Json<Product>) -> Product {
Product {
pid: p.pid,
description,
image,
in_stock,
name,
price,
qty,
description: String::from(&jp.description),
image: jp.image.iter().cloned().collect(),
in_stock: jp.in_stock,
name: String::from(&jp.name),
price: jp.price,
qty: jp.qty,
}
}
}
@ -485,7 +488,7 @@ impl Order {
xmr_address,
}
}
pub fn update(orid: String, o: Order) -> Order {
pub fn update(orid: String, o: &Json<Order>) -> Order {
Order {
orid,
cid: String::from(&o.cid),

View file

@ -208,6 +208,7 @@ pub fn contact_to_json(c: &models::Contact) -> Json<models::Contact> {
let r_contact: models::Contact = models::Contact {
cid: String::from(&c.cid),
i2p_address: String::from(&c.i2p_address),
is_vendor: c.is_vendor,
xmr_address: String::from(&c.xmr_address),
gpg_key: c.gpg_key.iter().cloned().collect(),
};

View file

@ -407,6 +407,7 @@ impl eframe::App for AddressBookApp {
let mut is_added = self.added;
let is_loading = self.is_loading;
let i2p_address = self.s_contact.i2p_address.clone();
let is_vendor = self.s_contact.is_vendor;
let xmr_address = self.s_contact.xmr_address.clone();
let gpg_key = self.s_contact.gpg_key.iter().cloned().collect();
@ -453,6 +454,7 @@ impl eframe::App for AddressBookApp {
let c_contact: models::Contact = models::Contact {
cid: self.s_contact.cid.clone(),
i2p_address,
is_vendor,
xmr_address,
gpg_key: self.s_contact.gpg_key.iter().cloned().collect(),
};

View file

@ -11,25 +11,32 @@ use nevmes_core::*;
use crate::{
dispute,
order,
product,
};
// JSON APIs
/// Create a product by passing vendor vid
/// Create a product by passings json product
#[post("/create", data = "<req_product>")]
pub async fn create_product(
req_product: Json<models::Product>,
_token: auth::BearerToken,
) -> Custom<Json<models::Product>> {
let m_product: models::Product = product::create(req_product);
Custom(Status::Created, Json(m_product))
}
/// Get a product by passing id
#[post("/<pid>")]
pub async fn get_product(pid: String, _token: auth::BearerToken) -> Custom<Json<models::Product>> {
let m_product: models::Product = product::find(&pid);
Custom(Status::Ok, Json(m_product))
}
/// Update product information
#[patch("/<_address>/update", data = "<product>")]
#[patch("/update", data = "<product>")]
pub async fn update_product(
_address: String,
product: Json<models::Product>,
_token: auth::BearerToken,
) -> Custom<Json<models::Product>> {
@ -37,51 +44,36 @@ pub async fn update_product(
Custom(Status::Ok, Json(m_product))
}
// /// Initialize order
// #[get("/<address>/create/<pid>")]
// pub async fn initialize_order(
// address: String,
// _token: auth::BearerToken,
// pid: String,
// ) -> Custom<Json<reqres::GetOrderResponse>> {
// // get the cid from the address after verification
// let m_customer = customer::find(address).await;
// let temp_pid = String::from(&pid);
// let m_order: models::Order = order::create(m_customer.cid, temp_pid).await;
// Custom(
// Status::Ok,
// Json(reqres::GetOrderResponse::build(pid, m_order)),
// )
// }
/// Return all products
#[patch("/")]
pub async fn get_products(_token: auth::BearerToken) -> Custom<Json<Vec<models::Product>>> {
let m_products: Vec<models::Product> = product::find_all();
Custom(Status::Ok, Json(m_products))
}
// /// Update order information from vendor
// #[patch("/update/<pid>/<oid>/<data>/vendor")]
// pub async fn update_order(
// _address: String,
// oid: String,
// pid: String,
// _token: auth::BearerToken,
// data: String,
// ) -> Custom<Json<reqres::GetOrderResponse>> {
// let temp_pid: String = String::from(&pid);
// let m_order: models::Order = order::modify(oid, pid, data, update_type).await;
// Custom(
// Status::Ok,
// Json(reqres::GetOrderResponse::build(temp_pid, m_order)),
// )
// }
/// Get a order by passing id
#[post("/<orid>")]
pub async fn get_order(orid: String, _token: auth::BearerToken) -> Custom<Json<models::Order>> {
let m_order: models::Order = order::find(&orid);
Custom(Status::Ok, Json(m_order))
}
// /// Get all orders
// /// by passing auth
// #[get("/<address>/<corv>")]
// pub async fn get_orders(
// address: String,
// corv: String,
// _token: auth::BearerToken,
// ) -> Custom<Json<reqres::GetOrdersResponse>> {
// let m_orders: Vec<models::Order> = order::find_all(address, corv).await;
// Custom(Status::Ok, Json(reqres::GetOrdersResponse::build(m_orders)))
// }
/// Get a order by passing id
#[post("/")]
pub async fn get_orders(_token: auth::BearerToken) -> Custom<Json<Vec<models::Order>>> {
let m_orders: Vec<models::Order> = order::find_all();
Custom(Status::Ok, Json(m_orders))
}
/// Update order information
#[patch("/update", data = "<order>")]
pub async fn update_order(
order: Json<models::Order>,
_token: auth::BearerToken,
) -> Custom<Json<models::Order>> {
let m_order: models::Order = order::modify(order);
Custom(Status::Ok, Json(m_order))
}
/// Create a dispute
#[post("/create", data = "<dispute>")]

View file

@ -19,8 +19,12 @@ async fn rocket() -> _ {
"/dispute",
routes![controller::create_dispute, controller::get_dispute],
)
// .mount("/order", routes![controller::initialize_order, controller::update_order])
// .mount("/orders", routes![controller::get_orders])
.mount(
"/order",
routes![controller::get_order, controller::update_order],
)
.mount("/orders", routes![controller::get_orders])
.mount("/products", routes![controller::get_products])
.mount(
"/product",
routes![controller::create_product, controller::update_product],

View file

@ -1,5 +1,15 @@
use nevmes_core::*;
use log::{debug, error, info};
use log::{
debug,
error,
info,
};
use nevmes_core::{
db,
models::*,
monero,
reqres,
utils,
};
use rocket::serde::json::Json;
enum StatusType {
@ -21,13 +31,13 @@ impl StatusType {
}
/// Create a intial order
pub async fn create(j_order: Json<reqres::OrderRequest>) -> models::Order {
pub async fn create(j_order: Json<reqres::OrderRequest>) -> Order {
info!("creating order");
let ts = chrono::offset::Utc::now().timestamp();
let orid: String = format!("O{}", utils::generate_rnd());
let r_subaddress = monero::create_address().await;
let subaddress = r_subaddress.result.address;
let new_order = models::Order {
let new_order = Order {
orid: String::from(&orid),
cid: String::from(&j_order.cid),
pid: String::from(&j_order.pid),
@ -47,7 +57,7 @@ pub async fn create(j_order: Json<reqres::OrderRequest>) -> models::Order {
debug!("insert order: {:?}", &new_order);
let s = db::Interface::open();
let k = &new_order.orid;
db::Interface::write(&s.env, &s.handle, k, &models::Order::to_db(&new_order));
db::Interface::write(&s.env, &s.handle, k, &Order::to_db(&new_order));
// in order to retrieve all orders, write keys to with ol
let list_key = format!("ol");
let r = db::Interface::read(&s.env, &s.handle, &String::from(&list_key));
@ -61,19 +71,39 @@ pub async fn create(j_order: Json<reqres::OrderRequest>) -> models::Order {
}
/// Lookup order
pub fn find(oid: String) -> models::Order {
pub fn find(oid: &String) -> Order {
info!("find order: {}", &oid);
let s = db::Interface::open();
let r = db::Interface::read(&s.env, &s.handle, &String::from(&oid));
let r = db::Interface::read(&s.env, &s.handle, &String::from(oid));
if r == utils::empty_string() {
error!("order not found");
return Default::default();
}
models::Order::from_db(String::from(&oid), r)
Order::from_db(String::from(oid), r)
}
/// Lookup all orders from admin server
pub fn find_all() -> Vec<Order> {
let i_s = db::Interface::open();
let i_list_key = format!("ol");
let i_r = db::Interface::read(&i_s.env, &i_s.handle, &String::from(i_list_key));
if i_r == utils::empty_string() {
error!("order index not found");
}
let i_v_oid = i_r.split(",");
let i_v: Vec<String> = i_v_oid.map(|s| String::from(s)).collect();
let mut orders: Vec<Order> = Vec::new();
for o in i_v {
let order: Order = find(&o);
if order.orid != utils::empty_string() {
orders.push(order);
}
}
orders
}
/// Lookup all orders for customer
pub async fn find_all_customer_orders(cid: String) -> Vec<models::Order> {
pub async fn find_all_customer_orders(cid: String) -> Vec<Order> {
info!("lookup orders for customer: {}", &cid);
let i_s = db::Interface::open();
let i_list_key = format!("ol");
@ -83,12 +113,27 @@ pub async fn find_all_customer_orders(cid: String) -> Vec<models::Order> {
}
let i_v_oid = i_r.split(",");
let i_v: Vec<String> = i_v_oid.map(|s| String::from(s)).collect();
let mut orders: Vec<models::Order> = Vec::new();
let mut orders: Vec<Order> = Vec::new();
for o in i_v {
let order: models::Order = find(o);
let order: Order = find(&o);
if order.orid != utils::empty_string() && order.cid == cid {
orders.push(order);
}
}
orders
}
/// Modify order from admin server
pub fn modify(o: Json<Order>) -> Order {
info!("modify order: {}", &o.orid);
let f_order: Order = find(&o.orid);
if f_order.orid == utils::empty_string() {
error!("order not found");
return Default::default();
}
let u_order = Order::update(String::from(&f_order.orid), &o);
let s = db::Interface::open();
db::Interface::delete(&s.env, &s.handle, &u_order.pid);
db::Interface::write(&s.env, &s.handle, &u_order.pid, &Order::to_db(&u_order));
return u_order;
}

View file

@ -81,15 +81,7 @@ pub fn modify(p: Json<Product>) -> Product {
error!("product not found");
return Default::default();
}
let u_prod = Product::update(
f_prod,
String::from(&p.description),
p.image.iter().cloned().collect(),
p.in_stock,
String::from(&p.description),
p.price,
p.qty,
);
let u_prod = Product::update(f_prod, &p);
let s = db::Interface::open();
db::Interface::delete(&s.env, &s.handle, &u_prod.pid);
db::Interface::write(&s.env, &s.handle, &u_prod.pid, &Product::to_db(&u_prod));

View file

@ -93,12 +93,13 @@ pub async fn get_products(_jwp: proof::PaymentProof) -> Custom<Json<Vec<models::
#[post("/order/create", data = "<r_order>")]
pub async fn create_order(
r_order: Json<reqres::OrderRequest>,
_jwp: proof::PaymentProof) -> Custom<Json<models::Order>> {
_jwp: proof::PaymentProof,
) -> Custom<Json<models::Order>> {
let m_order: models::Order = order::create(r_order).await;
Custom(Status::Created, Json(m_order))
}
/// Customer order retreival. Must send `signature`
/// TODO: Customer order retreival. Must send `signature`
///
/// which is the order id signed by the wallet.
///
@ -107,13 +108,13 @@ pub async fn create_order(
pub async fn retrieve_order(
orid: String,
_signature: String,
_jwp: proof::PaymentProof) -> Custom<Json<models::Order>> {
_jwp: proof::PaymentProof,
) -> Custom<Json<models::Order>> {
// get customer address
// send address, orid and signature to verify()
let m_order: models::Order = order::find(orid);
let m_order: models::Order = order::find(&orid);
Custom(Status::Created, Json(m_order))
}
@ -123,7 +124,8 @@ pub async fn retrieve_order(
#[post("/", data = "<r_info>")]
pub async fn get_multisig_info(
r_info: Json<reqres::MultisigInfoRequest>,
_jwp: proof::PaymentProof) -> Custom<Json<models::Order>> {
_jwp: proof::PaymentProof,
) -> Custom<Json<models::Order>> {
let info: Vec<String> = r_info.info.iter().cloned().collect();
if r_info.msig_type == String::from(message::PREPARE_MSIG) {
message::send_prepare_info(&r_info.orid, &r_info.contact).await;

View file

@ -55,10 +55,16 @@ async fn rocket() -> _ {
.mount("/multisig/info", routes![controller::get_multisig_info])
.mount("/invoice", routes![controller::gen_invoice])
.mount("/message/rx", routes![controller::rx_message])
.mount("/message/rx/multisig", routes![controller::rx_multisig_message])
.mount(
"/message/rx/multisig",
routes![controller::rx_multisig_message],
)
.mount("/prove", routes![controller::gen_jwp])
.mount("/share", routes![controller::share_contact_info])
.mount("/i2p", routes![controller::get_i2p_status])
.mount("/xmr/rpc", routes![controller::get_version])
.mount("/market", routes![controller::get_products])
.mount(
"/market",
routes![controller::create_order, controller::get_products],
)
}