Merge pull request #485 from gradido/community_transfer_confirmation_email

change transfer confirmation email
This commit is contained in:
einhornimmond 2021-06-08 17:30:46 +02:00 committed by GitHub
commit 07d7684e8e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 226 additions and 114 deletions

View File

@ -60,19 +60,21 @@ Router::scope('/', function (RouteBuilder $routes) {
$whitelist = ['JsonRequestHandler', 'ElopageWebhook', 'AppRequests']; $whitelist = ['JsonRequestHandler', 'ElopageWebhook', 'AppRequests'];
$ajaxWhitelist = ['TransactionSendCoins', 'TransactionCreations']; $ajaxWhitelist = ['TransactionSendCoins', 'TransactionCreations'];
$callerIp = $request->clientIp();
foreach($whitelist as $entry) { foreach($whitelist as $entry) {
if($request->getParam('controller') === $entry) { if($request->getParam('controller') === $entry) {
if($entry == 'ElopageWebhook' || $entry == 'AppRequests') { if($entry == 'ElopageWebhook' || $entry == 'AppRequests') {
return true; return true;
} }
$allowedIpLocalhost = ['127.0.0.1', 'localhost', '', '::1']; $allowedIpLocalhost = ['127.0.0.1', 'localhost', '', '::1'];
if(in_array($request->clientIp(), $allowedIpLocalhost)) { if(in_array($callerIp, $allowedIpLocalhost)) {
return true; return true;
} }
$allowedCaller = Configure::read('API.allowedCaller'); $allowedCaller = Configure::read('API.allowedCaller');
$ipPerHost = []; $ipPerHost = [];
if($allowedCaller && count($allowedCaller) > 0) { if($allowedCaller && count($allowedCaller) > 0) {
$callerIp = $request->clientIp();
foreach($allowedCaller as $allowed) { foreach($allowedCaller as $allowed) {
$ip = gethostbyname($allowed); $ip = gethostbyname($allowed);
$ipPerHost[$allowed] = $ip; $ipPerHost[$allowed] = $ip;

View File

@ -391,8 +391,12 @@ class JsonRequestHandlerController extends AppController {
} }
if ($transaction->save()) { if ($transaction->save()) {
$result = ['state' => 'success'];
if($transaction->hasWarnings()) {
$result['warnings'] = $transaction->getWarnings();
}
// success // success
return $this->returnJson(['state' => 'success']); return $this->returnJson($result);
} else { } else {
$this->sendEMailTransactionFailed($transaction, 'save'); $this->sendEMailTransactionFailed($transaction, 'save');

View File

@ -198,8 +198,10 @@ class Transaction extends TransactionBase {
$connection->commit(); $connection->commit();
$this->mTransactionBody->getSpecificTransaction()->sendNotificationEmail($this->mTransactionBody->getMemo()); $specificTransaction = $this->mTransactionBody->getSpecificTransaction();
$specificTransaction->sendNotificationEmail($this->mTransactionBody->getMemo());
$this->addWarnings($specificTransaction->getWarnings());
return true; return true;
} }

View File

@ -6,24 +6,38 @@ use Cake\ORM\TableRegistry;
class TransactionBase { class TransactionBase {
private $errors = []; private $errors = [];
private $warnings = [];
static $tables = []; static $tables = [];
public function getErrors() { public function getErrors() {
return $this->errors; return $this->errors;
} }
public function getWarnings() {
return $this->warnings;
}
public function addError($functionName, $errorName) { public function addError($functionName, $errorName) {
array_push($this->errors, [$functionName => $errorName]); array_push($this->errors, [$functionName => $errorName]);
} }
public function addWarning($functionName, $warningName) {
array_push($this->warnings, [$functionName => $warningName]);
}
public function addErrors($errors) { public function addErrors($errors) {
$this->errors = array_merge($this->errors, $errors); $this->errors = array_merge($this->errors, $errors);
} }
public function addWarnings($warnings) {
$this->warnings = array_merge($this->warnings, $warnings);
}
public function hasErrors() { public function hasErrors() {
return count($this->errors) > 0; return count($this->errors) > 0;
} }
public function hasWarnings() {
return count($this->warnings) > 0;
}
public static function getTable($tableName) { public static function getTable($tableName) {
if(!isset(self::$tables[$tableName])) { if(!isset(self::$tables[$tableName])) {
self::$tables[$tableName] = TableRegistry::getTableLocator()->get($tableName); self::$tables[$tableName] = TableRegistry::getTableLocator()->get($tableName);

View File

@ -209,6 +209,7 @@ class TransactionCreation extends TransactionBase {
->send(); ->send();
} catch(Exception $e) { } catch(Exception $e) {
// $this->addError('TransactionCreation::sendNotificationEmail', 'error sending notification email: ' . $e->getMessage()); // $this->addError('TransactionCreation::sendNotificationEmail', 'error sending notification email: ' . $e->getMessage());
$this->addWarning('TransactionCreation::sendNotificationEmail', 'error sending notification email: ' . $e->getMessage());
return false; return false;
} }
return true; return true;

View File

@ -204,13 +204,14 @@ class TransactionTransfer extends TransactionBase {
$this->addError('TransactionCreation::sendNotificationEmail', 'to email is empty for user: ' . $receiverUser->id); $this->addError('TransactionCreation::sendNotificationEmail', 'to email is empty for user: ' . $receiverUser->id);
return false; return false;
} }
$email->setFrom([$serverAdminEmail => $senderUser->getNames() . ' via Gradido Community']) $noReplyEmail = Configure::read('noReplyEmail');
$email->setFrom([$noReplyEmail => 'Gradido (nicht antworten)'])
->setTo([$receiverUser->email => $receiverUser->getNames()]) ->setTo([$receiverUser->email => $receiverUser->getNames()])
->setReplyTo($senderUser->email)
->setSubject(__('Gradidos erhalten')) ->setSubject(__('Gradidos erhalten'))
->send(); ->send();
} catch(Exception $e) { } catch(Exception $e) {
//$this->addError('TransactionTransfer::sendNotificationEmail', 'error sending notification email: ' . $e->getMessage()); //$this->addError('TransactionTransfer::sendNotificationEmail', 'error sending notification email: ' . $e->getMessage());
$this->addWarning('TransactionTransfer::sendNotificationEmail', 'error sending notification email: ' . $e->getMessage());
return false; return false;
} }
return true; return true;

View File

@ -15,7 +15,7 @@ $senderNames = $senderUser->first_name . ' ' . $senderUser->last_name;
<?= $memo ?> <?= $memo ?>
<?= __('Du kannst {0} eine Nachricht schreiben, indem du auf diese E-Mail antwortest', $senderNames); ?> <?= __('Bitte antworte nicht auf diese E-Mail!'); ?>
<?= __('Mit freundlichen Grüßen'); ?> <?= __('Mit freundlichen Grüßen'); ?>
Gradido Community Server Gradido Community Server

View File

@ -214,9 +214,8 @@ return [
'timeout' => 30, 'timeout' => 30,
'username' => null, 'username' => null,
'password' => null, 'password' => null,
'client' => null, 'className' => 'Smtp',
'tls' => null, 'tls' => true
'url' => env('EMAIL_TRANSPORT_DEFAULT_URL', null),
], ],
], ],

View File

@ -135,6 +135,7 @@ Poco::JSON::Object* JsonCreateTransaction::transfer(Poco::Dynamic::Var params)
else { else {
printf("user hasn't valid key pair set\n"); printf("user hasn't valid key pair set\n");
} }
Poco::JSON::Array* json_warnings = nullptr;
if (!result) { if (!result) {
try { try {
auto transaction = model::gradido::Transaction::createTransfer(sender_user, target_pubkey, mTargetGroup, amount, mMemo, mBlockchainType); auto transaction = model::gradido::Transaction::createTransfer(sender_user, target_pubkey, mTargetGroup, amount, mMemo, mBlockchainType);
@ -149,6 +150,10 @@ Poco::JSON::Object* JsonCreateTransaction::transfer(Poco::Dynamic::Var params)
if (errors.size() > 0) { if (errors.size() > 0) {
return stateError("error by signing transaction", errors); return stateError("error by signing transaction", errors);
} }
if (transaction->warningCount() > 0) {
json_warnings = new Poco::JSON::Array;
json_warnings->add(transaction->getWarningsArray());
}
} }
} }
catch (Poco::Exception& ex) { catch (Poco::Exception& ex) {
@ -164,6 +169,10 @@ Poco::JSON::Object* JsonCreateTransaction::transfer(Poco::Dynamic::Var params)
return stateError("exception"); return stateError("exception");
} }
result = stateSuccess(); result = stateSuccess();
if (json_warnings) {
result->set("warnings", json_warnings);
delete json_warnings;
}
} }
mm->releaseMemory(target_pubkey); mm->releaseMemory(target_pubkey);
return result; return result;

View File

@ -11,6 +11,7 @@
#include "sodium.h" #include "sodium.h"
#include "../SingletonManager/MemoryManager.h" #include "../SingletonManager/MemoryManager.h"
#include "DataTypeConverter.h" #include "DataTypeConverter.h"
#include "Warning.h"
JsonRequest::JsonRequest(const std::string& serverHost, int serverPort) JsonRequest::JsonRequest(const std::string& serverHost, int serverPort)
: mServerHost(serverHost), mServerPort(serverPort) : mServerHost(serverHost), mServerPort(serverPort)
@ -125,11 +126,19 @@ JsonRequestReturn JsonRequest::request(const char* methodName, const Poco::JSON:
return JSON_REQUEST_RETURN_ERROR; return JSON_REQUEST_RETURN_ERROR;
} }
else if (stateString == "success") { else if (stateString == "success") {
auto warnings_obj = object.get("warnings");
if (!warnings_obj.isEmpty()) {
Poco::JSON::Object warnings = *parsedJson.extract<Poco::JSON::Object::Ptr>();
for (auto it = warnings.begin(); it != warnings.end(); it++) {
addWarning(new Warning(it->first, it->second.toString()));
}
}
for (auto it = object.begin(); it != object.end(); it++) { for (auto it = object.begin(); it != object.end(); it++) {
if (it->first == "state") continue; if (it->first == "state") continue;
std::string index = it->first; std::string index = it->first;
std::string value = it->second.toString(); std::string value = it->second.toString();
printf("[JsonRequest] %s: %s\n", index.data(), value.data());
//printf("[JsonRequest] %s: %s\n", index.data(), value.data());
} }
} }
} }
@ -165,95 +174,5 @@ JsonRequestReturn JsonRequest::request(const char* methodName)
return request(methodName, requestJson); return request(methodName, requestJson);
} }
#include "Poco/JSON/Stringifier.h"
JsonRequestReturn JsonRequest::requestGRPCRelay(const Poco::Net::NameValueCollection& payload)
{
static const char* functionName = "JsonRequest::requestGRPCRelay";
Poco::JSON::Object requestJson;
for (auto it = payload.begin(); it != payload.end(); it++) {
requestJson.set(it->first, it->second);
}
// send post request via https
// 443 = HTTPS Default
// TODO: adding port into ServerConfig
try {
Profiler phpRequestTime;
Poco::Net::HTTPClientSession httpClientSession(mServerHost, mServerPort);
Poco::Net::HTTPRequest request(Poco::Net::HTTPRequest::HTTP_POST, "/hedera_rpc_relay/gRPCProxy.php");
request.setChunkedTransferEncoding(false);
std::ostream& requestStream = httpClientSession.sendRequest(request);
requestJson.stringify(requestStream);
std::stringstream ss;
requestJson.stringify(ss);
auto f = fopen("grpc.txt", "wt");
std::string grpc = ss.str();
fwrite(grpc.data(), grpc.size(), 1, f);
fclose(f);
Poco::Net::HTTPResponse response;
std::istream& request_stream = httpClientSession.receiveResponse(response);
// debugging answer
std::stringstream responseStringStream;
for (std::string line; std::getline(request_stream, line); ) {
responseStringStream << line << std::endl;
}
Poco::Logger& speedLog = Poco::Logger::get("SpeedLog");
speedLog.information("[gRPC relay] php server time: %s", phpRequestTime.string());
// extract parameter from request
Poco::JSON::Parser jsonParser;
Poco::Dynamic::Var parsedJson;
try {
parsedJson = jsonParser.parse(responseStringStream.str());
}
catch (Poco::Exception& ex) {
addError(new ParamError(functionName, "error parsing request answer grpc relay", ex.displayText().data()));
std::string fileName = "response_grpc_";
fileName += ".html";
FILE* f = fopen(fileName.data(), "wt");
std::string responseString = responseStringStream.str();
fwrite(responseString.data(), 1, responseString.size(), f);
fclose(f);
// */
sendErrorsAsEmail(responseStringStream.str());
return JSON_REQUEST_RETURN_PARSE_ERROR;
}
Poco::JSON::Object object = *parsedJson.extract<Poco::JSON::Object::Ptr>();
auto state = object.get("state");
std::string stateString = state.convert<std::string>();
if (stateString == "error") {
addError(new Error(functionName, "php server return error"));
if (!object.isNull("msg")) {
addError(new ParamError(functionName, "msg:", object.get("msg").convert<std::string>().data()));
}
if (!object.isNull("details")) {
addError(new ParamError(functionName, "details:", object.get("details").convert<std::string>().data()));
}
// send copy of errors as email, to have result also in db
sendErrorsAsEmail("", true);
return JSON_REQUEST_RETURN_ERROR;
}
ss.clear();
Poco::JSON::Stringifier::stringify(object, ss);
printf("json request result: %s\n", ss.str().data());
}
catch (Poco::Exception& e) {
addError(new ParamError(functionName, "connect error to php server", e.displayText().data()));
sendErrorsAsEmail();
return JSON_REQUEST_CONNECT_ERROR;
}
return JSON_REQUEST_RETURN_OK;
}

View File

@ -32,7 +32,6 @@ public:
JsonRequestReturn request(const char* methodName, const Poco::Net::NameValueCollection& payload); JsonRequestReturn request(const char* methodName, const Poco::Net::NameValueCollection& payload);
JsonRequestReturn request(const char* methodName, const Poco::JSON::Object& payload); JsonRequestReturn request(const char* methodName, const Poco::JSON::Object& payload);
JsonRequestReturn request(const char* methodName); JsonRequestReturn request(const char* methodName);
JsonRequestReturn requestGRPCRelay(const Poco::Net::NameValueCollection& payload);
protected: protected:
int mServerPort; int mServerPort;

View File

@ -11,3 +11,9 @@ Notification::Notification(const char* functionName, const std::string& message)
{ {
} }
Notification::Notification(const std::string& functionName, const std::string& message)
: mFunctionName(functionName), mMessage(message)
{
}

View File

@ -8,6 +8,7 @@ class Notification
public: public:
Notification(const char* functionName, const char* message); Notification(const char* functionName, const char* message);
Notification(const char* functionName, const std::string& message); Notification(const char* functionName, const std::string& message);
Notification(const std::string& functionName, const std::string& message);
const char* getFunctionName() { return mFunctionName.data(); } const char* getFunctionName() { return mFunctionName.data(); }
const char* getMessage() { return mMessage.data(); } const char* getMessage() { return mMessage.data(); }
@ -16,6 +17,7 @@ public:
virtual bool isError() { return false; } virtual bool isError() { return false; }
virtual bool isSuccess() { return false; } virtual bool isSuccess() { return false; }
virtual bool isWarning() { return false; }
protected: protected:
std::string mFunctionName; std::string mFunctionName;

View File

@ -54,6 +54,11 @@ NotificationList::~NotificationList()
delete mErrorStack.top(); delete mErrorStack.top();
mErrorStack.pop(); mErrorStack.pop();
} }
while (mWarningStack.size() > 0) {
delete mWarningStack.top();
mWarningStack.pop();
}
} }
void NotificationList::addError(Notification* error, bool log/* = true */) void NotificationList::addError(Notification* error, bool log/* = true */)
@ -61,12 +66,21 @@ void NotificationList::addError(Notification* error, bool log/* = true */)
if (log) { if (log) {
std::string dateTimeString = Poco::DateTimeFormatter::format(Poco::DateTime(), "%d.%m.%y %H:%M:%S"); std::string dateTimeString = Poco::DateTimeFormatter::format(Poco::DateTime(), "%d.%m.%y %H:%M:%S");
mLogging.error("%s [ErrorList::addError] %s", dateTimeString, error->getString(false)); mLogging.error("%s [NotificationList::addError] %s", dateTimeString, error->getString(false));
} }
mErrorStack.push(error); mErrorStack.push(error);
} }
void NotificationList::addWarning(Warning* warning, bool log/* = true*/)
{
if (log) {
std::string dateTimeString = Poco::DateTimeFormatter::format(Poco::DateTime(), "%d.%m.%y %H:%M:%S");
mLogging.warning("%s [NotificationList::addWarning] %s", dateTimeString, warning->getString(false));
}
mWarningStack.push(warning);
}
void NotificationList::addNotification(Notification* notification) void NotificationList::addNotification(Notification* notification)
{ {
mErrorStack.push(notification); mErrorStack.push(notification);
@ -86,6 +100,20 @@ Notification* NotificationList::getLastError()
return error; return error;
} }
Warning* NotificationList::getLastWarning()
{
if (mWarningStack.size() == 0) {
return nullptr;
}
Warning* warning = mWarningStack.top();
if (warning) {
mWarningStack.pop();
}
return warning;
}
void NotificationList::clearErrors() void NotificationList::clearErrors()
{ {
while (mErrorStack.size()) { while (mErrorStack.size()) {
@ -109,6 +137,17 @@ int NotificationList::getErrors(NotificationList* send)
return iCount; return iCount;
} }
int NotificationList::getWarnings(NotificationList* send)
{
Warning* warning = nullptr;
int iCount = 0;
while (warning = send->getLastWarning()) {
addWarning(warning, false);
iCount++;
}
return iCount;
}
void NotificationList::printErrors() void NotificationList::printErrors()
{ {
while (mErrorStack.size() > 0) { while (mErrorStack.size() > 0) {
@ -134,6 +173,21 @@ std::vector<std::string> NotificationList::getErrorsArray()
return result; return result;
} }
std::vector<std::string> NotificationList::getWarningsArray()
{
std::vector<std::string> result;
result.reserve(mWarningStack.size());
while (mWarningStack.size() > 0) {
auto warning = mWarningStack.top();
mWarningStack.pop();
//result->add(error->getString());
result.push_back(warning->getString());
delete warning;
}
return result;
}
std::string NotificationList::getErrorsHtml() std::string NotificationList::getErrorsHtml()
{ {
std::string res; std::string res;

View File

@ -11,6 +11,7 @@
#define DR_LUA_WEB_MODULE_ERROR_ERROR_LIST_H #define DR_LUA_WEB_MODULE_ERROR_ERROR_LIST_H
#include "Error.h" #include "Error.h"
#include "Warning.h"
#include <stack> #include <stack>
#include "../tasks/CPUTask.h" #include "../tasks/CPUTask.h"
@ -28,11 +29,14 @@ public:
// push error, error will be deleted in deconstructor // push error, error will be deleted in deconstructor
virtual void addError(Notification* error, bool log = true); virtual void addError(Notification* error, bool log = true);
void addNotification(Notification* notification); void addNotification(Notification* notification);
virtual void addWarning(Warning* warning, bool log = true);
// return error on top of stack, please delete after using // return error on top of stack, please delete after using
Notification* getLastError(); Notification* getLastError();
Warning* getLastWarning();
inline size_t errorCount() { return mErrorStack.size(); } inline size_t errorCount() { return mErrorStack.size(); }
inline size_t warningCount() { return mWarningStack.size(); }
// delete all errors // delete all errors
void clearErrors(); void clearErrors();
@ -41,16 +45,19 @@ public:
return recv->getErrors(send); return recv->getErrors(send);
} }
int getErrors(NotificationList* send); int getErrors(NotificationList* send);
int getWarnings(NotificationList* send);
void printErrors(); void printErrors();
std::string getErrorsHtml(); std::string getErrorsHtml();
std::string getErrorsHtmlNewFormat(); std::string getErrorsHtmlNewFormat();
std::vector<std::string> getErrorsArray(); std::vector<std::string> getErrorsArray();
std::vector<std::string> getWarningsArray();
void sendErrorsAsEmail(std::string rawHtml = "", bool copy = false); void sendErrorsAsEmail(std::string rawHtml = "", bool copy = false);
protected: protected:
std::stack<Notification*> mErrorStack; std::stack<Notification*> mErrorStack;
std::stack<Warning*> mWarningStack;
// poco logging // poco logging
Poco::Logger& mLogging; Poco::Logger& mLogging;
}; };

View File

@ -0,0 +1,58 @@
#include "Warning.h"
#include <sstream>
Warning::Warning(const char* functionName, const char* message)
: Notification(functionName, message)
{
}
Warning::Warning(const std::string& functionName, const std::string& message)
: Notification(functionName, message)
{
}
std::string Warning::getString(bool withNewline/* = true*/) const
{
std::stringstream ss;
ss << mFunctionName << ": " << mMessage;
if (withNewline) ss << std::endl;
return ss.str();
}
std::string Warning::getHtmlString() const
{
std::stringstream ss;
ss << mFunctionName << ": " << mMessage;
return ss.str();
}
ParamWarning::ParamWarning(const char* functionName, const char* message, std::string param)
: Warning(functionName, message), mParam(param)
{
}
ParamWarning::ParamWarning(const char* functionName, const char* message, int param)
: Warning(functionName, message), mParam(std::to_string(param))
{
}
std::string ParamWarning::getString(bool withNewline/* = true*/) const
{
std::stringstream ss;
ss << mFunctionName << ": " << mMessage << " " << mParam;
if (withNewline) ss << std::endl;
return ss.str();
}
std::string ParamWarning::getHtmlString() const
{
std::stringstream ss;
ss << mFunctionName << ": " << mMessage << " " << mParam;
return ss.str();
}

View File

@ -0,0 +1,31 @@
#ifndef GRADIDO_LOGIN_SERVER_LIB_WARNING_H
#define GRADIDO_LOGIN_SERVER_LIB_WARNING_H
#include "Notification.h"
class Warning : public Notification
{
public:
Warning(const char* functionName, const char* message);
Warning(const std::string& functionName, const std::string& message);
std::string getString(bool withNewline = true) const;
std::string getHtmlString() const;
virtual bool isWarning() { return true; }
};
class ParamWarning : public Warning
{
public:
ParamWarning(const char* functionName, const char* message, std::string param);
ParamWarning(const char* functionName, const char* message, int param);
std::string getString(bool withNewline = true) const;
std::string getHtmlString() const;
protected:
std::string mParam;
};
#endif //GRADIDO_LOGIN_SERVER_LIB_WARNING_H

View File

@ -561,7 +561,10 @@ namespace model {
Poco::Net::NameValueCollection param; Poco::Net::NameValueCollection param;
param.set("transaction", base_64_message); param.set("transaction", base_64_message);
auto result = json_request.request("putTransaction", param); auto result = json_request.request("putTransaction", param);
if (JSON_REQUEST_RETURN_OK == result) { json_request.getWarnings(&json_request);
if (JSON_REQUEST_RETURN_OK == result)
{
if (!json_request.errorCount()) { if (!json_request.errorCount()) {
finishSuccess(); finishSuccess();
} }
@ -572,6 +575,7 @@ namespace model {
return 1; return 1;
} }
json_request.getWarnings(&json_request);
getErrors(&json_request); getErrors(&json_request);
return -1; return -1;