16#include <unordered_set>
52 (std::wstring(appPath.begin(), appPath.end()) + L
"/mailer.log")
54 Logger::addOutput(std::wcout);
55 Logger::setLevel(Logger::L_TRACE);
56 Logger::setLevelFromEnv();
58 LOGI(this->
name.c_str() <<
", version: " << this->version.c_str());
60 LOGI(
"Logger initialized");
61 LOGI((std::string(
"Executable path: ") + appPath).c_str());
63 LOGT(
"Daemon constructor");
65 LOGT(
"Initializing INI file handler");
66 std::string iniFileName = appPath +
"/mailer.ini";
67 LOGI(
"INI path: " << iniFileName.c_str());
68 iniFile =
new mINI::INIFile(iniFileName);
71 mINI::INIStructure generatedIni;
72 generatedIni[
"Mailer"].set({{{
"sleepPeriod",
"30"}}});
73 generatedIni[
"MailSender"].set(
74 {{
"server",
"mail.gentoos.ru"},
76 {
"protocol",
"smtps"},
77 {
"sender",
"e.anisimov@gentoos.ru"},
78 {
"password",
"secret"},
79 {
"senderName",
"Eugene Anisimov"},
80 {
"ordersMail",
"orders@techno-liga74.ru"}});
81 generatedIni[
"Postgres"].set({{
"user",
"K"},
82 {
"password",
"secret"},
83 {
"host",
"localhost"},
86 {
"target_session_attrs",
"read-write"}});
87 if (
iniFile->generate(generatedIni,
false)) {
88 LOGI(
"Failed to read INI file " << iniFileName.c_str()
89 <<
", example generated");
91 LOGE(
"Failed to read INI file " << iniFileName.c_str());
95 LOGI(
"Registering program start");
98 }
catch (
const std::exception &e) {
99 LOGE(
"Failed to register program start" << std::endl << e.what());
105 LOGT(
"Daemon destructor");
107 LOGT(
"Disposing INI");
111 LOGI(
"Registering program stop");
114 }
catch (
const std::exception &e) {
115 LOGE(
"Failed to register program start" << std::endl << e.what());
118 LOGT(
"Disconnecting Postgres");
120 LOGT(
"Deinitializing EmailSender");
140 LOGE(
"Failed to read INI file");
147 }
catch (
const std::exception &e) {
155 LOGE(
"Error executing checkForSiteOrders");
158 LOGE(
"Error executing checkForSiteOrders");
161 LOGD(
"Bottom from sigsegv ");
162 LOGD(
"Trying to reincarnate");
171 std::string
name = NAME;
204 LOGI(
"Checking table " << tableName.c_str() <<
" for new orders");
209 res =
trx->exec(
"select key, \"N_Cat\", \"Name\", price, count, sent, "
210 "phone, email from " +
211 tableName +
" where sent=false");
212 }
catch (
const std::exception &e) {
213 LOGE(
"Failed to select from " << tableName.c_str());
219 std::unordered_set<std::string> keys;
220 for (
auto const &row : res) {
222 if (keys.contains(std::string(row[
"key"].c_str())))
225 LOGD(
"Sending mail to " << row[
"email"].c_str()
226 <<
" with ID: " << row[
"key"].c_str());
228 LOGD(
"Sending email with ID: " << row[
"key"].c_str() <<
" for "
229 << row[
"email"].c_str());
232 ini[
"EmailSender"][
"server"],
233 std::atoi(
ini[
"EmailSender"][
"port"].c_str()),
234 ini[
"EmailSender"][
"protocol"],
ini[
"EmailSender"][
"sender"],
235 ini[
"EmailSender"][
"password"],
ini[
"EmailSender"][
"senderName"]);
237 email_sender.
addCcEmailAddr(row[
"email"].c_str(), row[
"phone"].c_str());
240 std::string subject =
241 std::string(
"Заявка ") + row[
"key"].c_str() +
242 (tableName ==
"tab_siteOrders" ?
" с сайта"
243 :
" из мобильного приложения");
246 std::string(
"e-mail: <b>") + row[
"email"].c_str() +
247 "</b><br>\nphone: <b>" + row[
"phone"].c_str() +
249 "<table border=\"1\">\n"
251 "номер</td><td>Кол-во</td><td>Цена</td><td>Наименование</td></tr>\n";
252 for (
auto const &row_inner : res) {
253 if (row_inner[
"key"] == row[
"key"])
254 body += std::string(
"<tr>\n<td>") + row_inner[
"\"N_Cat\""].c_str() +
255 "</td>\n<td>" + row_inner[
"count"].c_str() +
"</td>\n<td>" +
256 row_inner[
"price"].c_str() +
"</td>\n<td>" +
257 row_inner[
"\"Name\""].c_str() +
"</td>\n</tr>\n";
263 status = email_sender.
send();
266 LOGD(
"Updating email status for ID: " << row[
"key"].c_str());
267 trx->exec_params(
"update " + tableName +
" set sent=true where key=$1",
271 LOGD(
"Failed to send mail for ID: " << row[
"key"].c_str());
275 keys.insert(row[
"key"].c_str());
287 std::string connStr =
288 "user=" +
ini[
"Postgres"][
"user"] +
" " +
289 "password=" +
ini[
"Postgres"][
"password"] +
" " +
290 "host=" +
ini[
"Postgres"][
"host"] +
" " +
291 "port=" +
ini[
"Postgres"][
"port"] +
" " +
292 "dbname=" +
ini[
"Postgres"][
"dbname"] +
" " +
293 "target_session_attrs=" +
ini[
"Postgres"][
"target_session_attrs"];
295 LOGT(
"Not reconnecting to PG, connection already established, "
296 "connectionStr isn't changed");
303 LOGI(
"Connecting to Postgresql");
305 }
catch (
const std::exception &e) {
306 throw std::runtime_error(e.what());
312 LOGI(
"Disconnecting Postgresql");
321 LOGT(
"Starting transaction");
327 LOGT(
"Aborting transaction");
335 LOGT(
"Committing transaction");
349 LOGI(
"Sleeping for " <<
sleepPeriod <<
" seconds");
373 }
catch (
const std::exception &e) {
383 }
catch (
const std::exception &e) {
396 LOGD(
"Turning off daemon");
397 LOGD(
" Signal was " + signal);
408 LOGD(
" Signal was " + signal);
419 return GetModuleFilename(
nullptr);
421 char result[PATH_MAX];
422 ssize_t count = readlink(
"/proc/self/exe", result, PATH_MAX);
423 std::string appPath = std::string(result, (count > 0) ? count : 0);
424 std::size_t found = appPath.find_last_of(
"/\\");
425 return appPath.substr(0, found);
void CommitTransaction()
Фиксирует текущую транзакцию.
mINI::INIFile * iniFile
INI-файл конфигурации.
void StartTransaction()
Создаёт новую транзакцию PostgreSQL.
volatile std::atomic< bool > running
Флаг работы демона.
std::string connectionStr
Строка подключения к PostgreSQL.
void DisconnectPg()
Отключается от PostgreSQL.
void AbortTransaction()
Откатывает текущую транзакцию.
pqxx::connection * connection
Соединение с PostgreSQL.
pqxx::work * trx
Текущая транзакция.
Products::ProductsLog * productsLog
Логирование старта/стопа в БД.
mINI::INIStructure ini
Структура INI-данных в памяти.
void Stop()
Останавливает основной цикл демона.
~Daemon()
Деструктор. Освобождает INI-файл, логгирует остановку, отключается от PG.
int sleepPeriod
Пауза между итерациями (сек).
int sendOrders(std::string tableName)
void addCcEmailAddr(const std::string &email_addr, const std::string &name="")
Добавляет получателя в скрытую копию.
int send()
Отправляет письмо через SMTP (libcurl).
void addRecvEmailAddr(const std::string &email_addr, const std::string &name="")
Добавляет получателя в список "Кому".
void setEmailContent(const std::string &subject="", const std::string &body="")
Устанавливает тему и тело письма.
jmp_buf return_to_main
jmp_buf для graceful shutdown (SIGINT/SIGTERM).
static std::string getApplicationDirectory()
Возвращает директорию, в которой находится исполняемый файл.
static Daemon * d
Глобальный указатель на демон (доступен из signal handler'ов).
jmp_buf return_to_bottom
jmp_buf для перезапуска цикла после SIGSEGV.
volatile std::sig_atomic_t gSignalStatus
Глобальный флаг последнего полученного сигнала.
static void sigIntHandler(int signal)
Обработчик сигналов завершения (SIGINT/SIGTERM/SIGABRT/SIGFPE/SIGILL).
static void sigsegvhandler(int signal)
Обработчик segmentation fault — перезапускает цикл.