diff --git a/services/user/Webmail/CMakeLists.txt b/services/user/Webmail/CMakeLists.txt deleted file mode 100644 index 447df2a31..000000000 --- a/services/user/Webmail/CMakeLists.txt +++ /dev/null @@ -1,7 +0,0 @@ -function(add suffix) - add_user_service("${suffix}" Webmail src/Webmail.cpp) -endfunction(add) - -conditional_add() - -set(CMAKE_EXPORT_COMPILE_COMMANDS on) \ No newline at end of file diff --git a/services/user/Webmail/Cargo.toml b/services/user/Webmail/Cargo.toml index 774e87a5c..e56967a2a 100644 --- a/services/user/Webmail/Cargo.toml +++ b/services/user/Webmail/Cargo.toml @@ -21,7 +21,7 @@ strip = true lto = true [package.metadata.psibase] -package-name = "webmail" +package-name = "Webmail" description = "Basic email app" services = ["webmail"] diff --git a/services/user/Webmail/service/src/lib.rs b/services/user/Webmail/service/src/lib.rs index 25d32c545..f76995298 100644 --- a/services/user/Webmail/service/src/lib.rs +++ b/services/user/Webmail/service/src/lib.rs @@ -1,18 +1,17 @@ use std::collections::HashMap; +use psibase::check; use psibase::services::accounts::Wrapper as AccountsSvc; use psibase::services::r_events::Wrapper as REventsSvc; use psibase::AccountNumber; use psibase::HttpReply; use psibase::HttpRequest; -fn validate_user(user: String) -> bool { - let acc = AccountNumber::from(user.as_str()); - if acc.to_string() != user { - return false; - } - - AccountsSvc::call().exists(acc) +fn validate_user(acct: AccountNumber) { + check( + AccountsSvc::call().exists(acct), + format!("account '{}' DNE", acct).as_str(), + ); } fn make_query(req: &HttpRequest, sql: &str) -> HttpRequest { @@ -56,18 +55,14 @@ fn serve_rest_api(request: &HttpRequest) -> Option { let mut s_clause = String::new(); let s_opt = params.get(&String::from("sender")); if let Some(s) = s_opt { - if !validate_user(String::from(s)) { - return None; - } + validate_user(AccountNumber::from(s.as_str())); s_clause = format!("sender = '{}'", s); } let mut r_clause = String::new(); let r_opt = params.get(&String::from("receiver")); if let Some(r) = r_opt { - if !validate_user(String::from(r)) { - return None; - } + validate_user(AccountNumber::from(r.as_str())); r_clause = format!("receiver = '{}'", r); } @@ -80,7 +75,7 @@ fn serve_rest_api(request: &HttpRequest) -> Option { where_clause += s_clause.as_str(); } if s_opt.is_some() && r_opt.is_some() { - where_clause += " AND WHERE "; + where_clause += " AND "; } if r_opt.is_some() { where_clause += r_clause.as_str(); @@ -113,10 +108,7 @@ mod service { #[action] fn send(receiver: AccountNumber, subject: String, body: String) { - check( - validate_user(receiver.to_string()), - &format!("receiver account {} doesn't exist", receiver), - ); + validate_user(receiver); Wrapper::emit() .history() .sent(get_sender(), receiver, subject, body); diff --git a/services/user/Webmail/src/Webmail.cpp b/services/user/Webmail/src/Webmail.cpp deleted file mode 100644 index a88b25068..000000000 --- a/services/user/Webmail/src/Webmail.cpp +++ /dev/null @@ -1,181 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace UserService; -using namespace psibase; -using namespace std; - -namespace -{ - HttpRequest makeQuery(const HttpRequest& in, std::string_view s) - { - return {.host = in.host, - .rootHost = in.rootHost, - .method = "POST", - .target = "/sql", - .contentType = "application/sql", - .body = std::vector(s.begin(), s.end())}; - } - - auto validateUser = [](const std::string& user) - { - auto acc = AccountNumber{user}; - if (acc.str() != user) - return false; - - return to().exists(acc); - }; - - map parseQuery(const string& query) - { - map params; - istringstream queryStream(query); - string pair; - - while (getline(queryStream, pair, '&')) - { - size_t pos = pair.find('='); - if (pos != string::npos) - { - string key = pair.substr(0, pos); - string value = pair.substr(pos + 1); - params[key] = value; - } - } - return params; - } -} // namespace - -Webmail::Webmail(psio::shared_view_ptr action) -{ - MethodNumber m{action->method()}; - if (m != "init"_m && m != "storeSys"_m) - { - auto initRecord = Tables().open().get(SingletonKey{}); - check(initRecord.has_value(), Errors::uninitialized); - } -} - -void Webmail::init() -{ - auto initTable = Tables().open(); - auto init = (initTable.get(SingletonKey{})); - check(not init.has_value(), Errors::alreadyInit); - initTable.put(InitializedRecord{}); - - // Configure manualDebit for self on Token and NFT - to().setUserConf("manualDebit"_m, true); - to().setUserConf("manualDebit"_m, true); - - // Register http server - to().registerServer(Webmail::service); - - // Events/indices - to().setSchema(ServiceSchema::make()); - to().addIndex(DbId::historyEvent, Webmail::service, "sent"_m, 0); - to().addIndex(DbId::historyEvent, Webmail::service, "sent"_m, 1); -} - -void Webmail::send(psibase::AccountNumber receiver, - const std::string& subject, - const std::string& body) -{ - check(to().exists(receiver), "target receiver account does not exist"); - emit().history().sent(getSender(), receiver, subject, body); -} - -// Supported REST query: -// `GET /messages?sender=alice&receiver=bob` -// Either sender or receiver (or both) must be specified. -optional serveRestApi(const HttpRequest& request) -{ - if (request.method == "GET") - { - if (!request.target.starts_with("/messages")) - { - return nullopt; - } - - size_t queryStart = request.target.find('?'); - if (queryStart == string::npos) - { - return nullopt; - } - - string query = request.target.substr(queryStart + 1); - auto params = parseQuery(query); - - string s, r; - if (params.count("sender") != 0) - { - auto user = params.at("sender"); - if (!validateUser(user)) - { - return nullopt; - } - s = "sender = '" + user + "'"; - } - if (params.count("receiver") != 0) - { - auto user = params.at("receiver"); - if (!validateUser(user)) - { - return nullopt; - } - r = "receiver = '" + user + "'"; - } - if (s.empty() && r.empty()) - { - return nullopt; - } - - string where = "WHERE " + s; - if (!s.empty() && !r.empty()) - { - where += " AND " + r; - } - else if (!r.empty()) - { - where = "WHERE " + r; - } - - return to().serveSys(makeQuery( - request, "SELECT * FROM \"history.webmail.sent\" " + where + " ORDER BY ROWID")); - } - return nullopt; -} - -optional Webmail::serveSys(HttpRequest request) -{ - if (auto result = serveRestApi(request)) - return result; - if (auto result = serveSimpleUI(request)) - return result; - if (request.target.ends_with(".html") || request.target == "/" || request.target.find('.') == request.target.npos) { - request.target = "/"; - } - if (auto result = serveContent(request, Tables{})) - return result; - - return nullopt; -} - -void Webmail::storeSys(string path, string contentType, vector content) -{ - check(getSender() == getReceiver(), "wrong sender"); - storeContent(std::move(path), std::move(contentType), std::move(content), Tables{}); -} - -PSIBASE_DISPATCH(UserService::Webmail)