diff --git a/Dockerfile b/Dockerfile index 778e9da..fc63be7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -55,7 +55,7 @@ RUN chmod +x /var/www/html/startup.sh # Expose port 80 for Nginx EXPOSE 80 -ARG SOFTWARE_VERSION=0.9.0 +ARG SOFTWARE_VERSION=1.0.0 # Start both PHP-FPM, Nginx CMD ["sh", "-c", "/var/www/html/startup.sh"] \ No newline at end of file diff --git a/README.md b/README.md index 00d7566..0e4b684 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,7 @@ See instructions to run Wallos below. - curl - gd - imagick + - openssl - sqlite3 #### Docker @@ -63,10 +64,13 @@ Download or clone this repo and move the files into your web root - usually `/va Add the following scripts to your cronjobs with `crontab -e` ```bash -0 0 * * * php /var/www/html/endpoints/cronjobs/updatenextpayment.php >> /var/log/cron/updatenextpayment.log 2>&1 -0 1 * * * php /var/www/html/endpoints/cronjobs/updateexchange.php >> /var/log/cron/updateexchange.log 2>&1 +0 1 * * * php /var/www/html/endpoints/cronjobs/updatenextpayment.php >> /var/log/cron/updatenextpayment.log 2>&1 +0 2 * * * php /var/www/html/endpoints/cronjobs/updateexchange.php >> /var/log/cron/updateexchange.log 2>&1 +0 9 * * * php /var/www/html/endpoints/cronjobs/sendnotifications.php >> /var/log/cron/sendnotifications.log 2>&1 ``` +If your web root is not `/var/www/html/` adjust both the cronjobs above and `/endpoints/cronjobs/conf.php` accordingly. + #### Docker ```bash diff --git a/cronjobs b/cronjobs index d3049a6..0360361 100644 --- a/cronjobs +++ b/cronjobs @@ -1,3 +1,4 @@ -# Run the script every day -0 0 * * * /usr/local/bin/php /var/www/html/endpoints/cronjobs/updatenextpayment.php >> /var/log/cron/updatenextpayment.log 2>&1 -0 1 * * * /usr/local/bin/php /var/www/html/endpoints/cronjobs/updateexchange.php >> /var/log/cron/updateexchange.log 2>&1 +# Run the scripts every day +0 1 * * * /usr/local/bin/php /var/www/html/endpoints/cronjobs/updatenextpayment.php >> /var/log/cron/updatenextpayment.log 2>&1 +0 2 * * * /usr/local/bin/php /var/www/html/endpoints/cronjobs/updateexchange.php >> /var/log/cron/updateexchange.log 2>&1 +0 9 * * * /usr/local/bin/php /var/www/html/endpoints/cronjobs/sendnotifications.php >> /var/log/cron/sendnotifications.log 2>&1 diff --git a/db/commands.sql b/db/commands.sql index 38c1248..4872b96 100644 --- a/db/commands.sql +++ b/db/commands.sql @@ -27,6 +27,7 @@ CREATE TABLE subscriptions ( payment_method_id INTEGER, payer_user_id INTEGER, category_id INTEGER, + notify BOOLEAN DEFAULT false, FOREIGN KEY(currency_id) REFERENCES currencies(id), FOREIGN KEY(cycle) REFERENCES cycles(id), FOREIGN KEY(frequency) REFERENCES frequencies(id), @@ -81,7 +82,17 @@ CREATE TABLE last_exchange_update ( CREATE TABLE last_update_next_payment_date ( date DATE NOT NULL -) +); + +CREATE TABLE notifications ( + id INTEGER PRIMARY KEY, + enabled BOOLEAN DEFAULT false, + days INTEGER, + smtp_address VARCHAR(255), + smtp_port INTEGER, + smtp_username VARCHAR(255), + smtp_password VARCHAR(255) +); INSERT INTO categories (id, name) VALUES (1, 'No category'), diff --git a/db/wallos.db b/db/wallos.db index f135a49..2057906 100644 Binary files a/db/wallos.db and b/db/wallos.db differ diff --git a/endpoints/cronjobs/conf.php b/endpoints/cronjobs/conf.php new file mode 100644 index 0000000..eabfc2e --- /dev/null +++ b/endpoints/cronjobs/conf.php @@ -0,0 +1,6 @@ + \ No newline at end of file diff --git a/endpoints/cronjobs/createdatabase.php b/endpoints/cronjobs/createdatabase.php index 4a7df26..3459dbe 100644 --- a/endpoints/cronjobs/createdatabase.php +++ b/endpoints/cronjobs/createdatabase.php @@ -1,9 +1,11 @@ busyTimeout(5000); @@ -36,6 +38,7 @@ if (!file_exists($databaseFile)) { payment_method_id INTEGER, payer_user_id INTEGER, category_id INTEGER, + notify BOOLEAN DEFAULT false, FOREIGN KEY(currency_id) REFERENCES currencies(id), FOREIGN KEY(cycle) REFERENCES cycles(id), FOREIGN KEY(frequency) REFERENCES frequencies(id), @@ -92,6 +95,16 @@ if (!file_exists($databaseFile)) { date DATE NOT NULL )'); + $db-exec('CREATE TABLE notifications ( + id INTEGER PRIMARY KEY, + enabled BOOLEAN DEFAULT false, + days INTEGER, + smtp_address VARCHAR(255), + smtp_port INTEGER, + smtp_username VARCHAR(255), + smtp_password VARCHAR(255) + )'); + $db->exec("INSERT INTO categories (id, name) VALUES (1, 'No category'), (2, 'Entertainment'), @@ -218,8 +231,53 @@ if (!file_exists($databaseFile)) { (30, 'VeriFone', 'verifone.png'), (31, 'WebMoney', 'webmoney.png')"); + echo "Database created.\n"; } else { - echo "Database already exist. Skipping..."; + echo "Database already exist. Checking for upgrades...\n"; + + $databaseFile = $webPath . 'db/wallos.db'; + $db = new SQLite3($databaseFile); + $db->busyTimeout(5000); + + if (!$db) { + die('Connection to the database failed.'); + } + + # v0.9 to v1.0 + # Added new notifications table + # Added notify column to subscriptions table + + $result = $db->query("SELECT name FROM sqlite_master WHERE type='table' AND name='notifications'"); + if (!$result->fetchArray(SQLITE3_ASSOC)) { + $db->exec('CREATE TABLE notifications ( + id INTEGER PRIMARY KEY, + enabled BOOLEAN DEFAULT false, + days INTEGER, + smtp_address VARCHAR(255), + smtp_port INTEGER, + smtp_username VARCHAR(255), + smtp_password VARCHAR(255) + )'); + echo "Table 'notifications' created.\n"; + } else { + echo "Table 'notifications' already exists.\n"; + } + + $result = $db->query("PRAGMA table_info(subscriptions)"); + $notifyColumnExists = false; + while ($row = $result->fetchArray(SQLITE3_ASSOC)) { + if ($row['name'] === 'notify') { + $notifyColumnExists = true; + break; + } + } + if (!$notifyColumnExists) { + $db->exec('ALTER TABLE subscriptions ADD COLUMN notify BOOLEAN DEFAULT false'); + echo "Column 'notify' added to table 'subscriptions'.\n"; + } else { + echo "Column 'notify' already exists in table 'subscriptions'.\n"; + } + } ?> \ No newline at end of file diff --git a/endpoints/cronjobs/sendnotifications.php b/endpoints/cronjobs/sendnotifications.php new file mode 100644 index 0000000..bbe7e1c --- /dev/null +++ b/endpoints/cronjobs/sendnotifications.php @@ -0,0 +1,89 @@ +query($query); + + if ($row = $result->fetchArray(SQLITE3_ASSOC)) { + $notificationsEnabled = $row['enabled']; + $days = $row['days']; + $smtpAddress = $row["smtp_address"]; + $smtpPort = $row["smtp_port"]; + $smtpUsername = $row["smtp_username"]; + $smtpPassword = $row["smtp_password"]; + } else { + echo "Notifications are disabled. No need to run."; + } + + if ($notificationsEnabled) { + $currencies = array(); + $query = "SELECT * FROM currencies"; + $result = $db->query($query); + while ($row = $result->fetchArray(SQLITE3_ASSOC)) { + $currencyId = $row['id']; + $currencies[$currencyId] = $row; + } + + $querySubscriptions = "SELECT * FROM subscriptions WHERE notify = 1"; + $resultSubscriptions = $db->query($querySubscriptions); + + $notify = []; $i = 0; + $currentDate = new DateTime('now'); + while ($rowSubscription = $resultSubscriptions->fetchArray(SQLITE3_ASSOC)) { + $nextPaymentDate = new DateTime($rowSubscription['next_payment']); + $difference = $currentDate->diff($nextPaymentDate)->days + 1; + if ($difference === $days) { + $notify[$i]['name'] = $rowSubscription['name']; + $notify[$i]['price'] = $rowSubscription['price'] . $currencies[$rowSubscription['currency_id']]['symbol']; + $i++; + } + } + + if (!empty($notify)) { + require $webPath . 'libs/PHPMailer/PHPMailer.php'; + require $webPath . 'libs/PHPMailer/SMTP.php'; + require $webPath . 'libs/PHPMailer/Exception.php'; + + $dayText = $days == 1 ? "tomorrow" : "in " . $days . " days" + $message = "The following subscriptions are up for renewal " . $dayText . ":\n"; + foreach ($notify as $subscription) { + $message .= $subscription['name'] . " for " . $subscription['price'] . "\n"; + } + + $mail = new PHPMailer(true); + $mail->isSMTP(); + + $mail->Host = $smtpAddress; + $mail->SMTPAuth = true; + $mail->Username = $smtpUsername; + $mail->Password = $smtpPassword; + $mail->SMTPSecure = 'tls'; + $mail->Port = $smtpPort; + + $getUser = "SELECT * FROM user WHERE id = 1"; + $user = $db->querySingle($getUser, true); + $email = $user['email']; + $name = $user['username']; + + $mail->setFrom('wallos@wallosapp.com', 'Wallos App'); + $mail->addAddress($email, $name); + + $mail->Subject = 'Wallos Notification'; + $mail->Body = $message; + + if ($mail->send()) { + echo "Notifications sent"; + } else { + echo "Error sending notifications: " . $mail->ErrorInfo; + } + } else { + echo "Nothing to notify."; + } + + } +?> \ No newline at end of file diff --git a/endpoints/cronjobs/updateexchange.php b/endpoints/cronjobs/updateexchange.php index 25c3589..8065cf6 100644 --- a/endpoints/cronjobs/updateexchange.php +++ b/endpoints/cronjobs/updateexchange.php @@ -1,5 +1,6 @@ query($query); diff --git a/endpoints/cronjobs/updatenextpayment.php b/endpoints/cronjobs/updatenextpayment.php index 8902e70..546939e 100644 --- a/endpoints/cronjobs/updatenextpayment.php +++ b/endpoints/cronjobs/updatenextpayment.php @@ -1,5 +1,6 @@ format('Y-m-d'); diff --git a/endpoints/notifications/save.php b/endpoints/notifications/save.php new file mode 100644 index 0000000..de52543 --- /dev/null +++ b/endpoints/notifications/save.php @@ -0,0 +1,71 @@ + false, + "errorMessage" => "Please fill all fields" + ]; + echo json_encode($response); + } else { + $enabled = $data["enabled"]; + $days = $data["days"]; + $smtpAddress = $data["smtpaddress"]; + $smtpPort = $data["smtpport"]; + $smtpUsername = $data["smtpusername"]; + $smtpPassword = $data["smtppassword"]; + + $query = "SELECT COUNT(*) FROM notifications"; + $result = $db->querySingle($query); + + if ($result === false) { + $response = [ + "success" => false, + "errorMessage" => "Error saving notifications data" + ]; + echo json_encode($response); + } else { + if ($result == 0) { + $query = "INSERT INTO notifications (enabled, days, smtp_address, smtp_port, smtp_username, smtp_password) + VALUES (:enabled, :days, :smtpAddress, :smtpPort, :smtpUsername, :smtpPassword)"; + } else { + $query = "UPDATE notifications + SET enabled = :enabled, days = :days, smtp_address = :smtpAddress, smtp_port = :smtpPort, + smtp_username = :smtpUsername, smtp_password = :smtpPassword"; + } + + $stmt = $db->prepare($query); + $stmt->bindValue(':enabled', $enabled, SQLITE3_INTEGER); + $stmt->bindValue(':days', $days, SQLITE3_INTEGER); + $stmt->bindValue(':smtpAddress', $smtpAddress, SQLITE3_TEXT); + $stmt->bindValue(':smtpPort', $smtpPort, SQLITE3_INTEGER); + $stmt->bindValue(':smtpUsername', $smtpUsername, SQLITE3_TEXT); + $stmt->bindValue(':smtpPassword', $smtpPassword, SQLITE3_TEXT); + + if ($stmt->execute()) { + $response = [ + "success" => true + ]; + echo json_encode($response); + } else { + $response = [ + "success" => false, + "errorMessage" => "Error saving notification data" + ]; + echo json_encode($response); + } + } + } + } +?> \ No newline at end of file diff --git a/endpoints/notifications/sendtestmail.php b/endpoints/notifications/sendtestmail.php new file mode 100644 index 0000000..239d7f6 --- /dev/null +++ b/endpoints/notifications/sendtestmail.php @@ -0,0 +1,72 @@ + false, + "errorMessage" => "Please fill all fields" + ]; + echo json_encode($response); + } else { + require '../../libs/PHPMailer/PHPMailer.php'; + require '../../libs/PHPMailer/SMTP.php'; + require '../../libs/PHPMailer/Exception.php'; + + $smtpAddress = $data["smtpaddress"]; + $smtpPort = $data["smtpport"]; + $smtpUsername = $data["smtpusername"]; + $smtpPassword = $data["smtppassword"]; + + $mail = new PHPMailer(true); + $mail->isSMTP(); + + $mail->Host = $smtpAddress; + $mail->SMTPAuth = true; + $mail->Username = $smtpUsername; + $mail->Password = $smtpPassword; + $mail->SMTPSecure = 'tls'; + $mail->Port = $smtpPort; + + $getUser = "SELECT * FROM user WHERE id = 1"; + $user = $db->querySingle($getUser, true); + $email = $user['email']; + $name = $user['username']; + + $mail->setFrom('wallos@wallosapp.com', 'Wallos App'); + $mail->addAddress($email, $name); + + $mail->Subject = 'Wallos Notification'; + $mail->Body = 'This is a test notification. If you\'re seeing this, the configuration is correct.'; + + if ($mail->send()) { + $response = [ + "success" => true, + ]; + echo json_encode($response); + } else { + $response = [ + "success" => false, + "errorMessage" => "Error sending email." . $mail->ErrorInfo + ]; + echo json_encode($response); + } + + } +} + +?> diff --git a/endpoints/subscription/add.php b/endpoints/subscription/add.php index 53671da..003a386 100644 --- a/endpoints/subscription/add.php +++ b/endpoints/subscription/add.php @@ -144,6 +144,7 @@ $notes = $_POST["notes"]; $logoUrl = $_POST['logo-url']; $logo = ""; + $notify = isset($_POST['notifications']) ? true : false; if($logoUrl !== "") { $logo = getLogoFromUrl($logoUrl, '../../images/uploads/logos/', $name); @@ -155,15 +156,15 @@ if (!$isEdit) { $sql = "INSERT INTO subscriptions (name, logo, price, currency_id, next_payment, cycle, frequency, notes, - payment_method_id, payer_user_id, category_id) + payment_method_id, payer_user_id, category_id, notify) VALUES (:name, :logo, :price, :currencyId, :nextPayment, :cycle, :frequency, :notes, - :paymentMethodId, :payerUserId, :categoryId)"; + :paymentMethodId, :payerUserId, :categoryId, :notify)"; } else { $id = $_POST['id']; if ($logo != "") { - $sql = "UPDATE subscriptions SET name = :name, logo = :logo, price = :price, currency_id = :currencyId, next_payment = :nextPayment, cycle = :cycle, frequency = :frequency, notes = :notes, payment_method_id = :paymentMethodId, payer_user_id = :payerUserId, category_id = :categoryId WHERE id = :id"; + $sql = "UPDATE subscriptions SET name = :name, logo = :logo, price = :price, currency_id = :currencyId, next_payment = :nextPayment, cycle = :cycle, frequency = :frequency, notes = :notes, payment_method_id = :paymentMethodId, payer_user_id = :payerUserId, category_id = :categoryId, notify = :notify WHERE id = :id"; } else { - $sql = "UPDATE subscriptions SET name = :name, price = :price, currency_id = :currencyId, next_payment = :nextPayment, cycle = :cycle, frequency = :frequency, notes = :notes, payment_method_id = :paymentMethodId, payer_user_id = :payerUserId, category_id = :categoryId WHERE id = :id"; + $sql = "UPDATE subscriptions SET name = :name, price = :price, currency_id = :currencyId, next_payment = :nextPayment, cycle = :cycle, frequency = :frequency, notes = :notes, payment_method_id = :paymentMethodId, payer_user_id = :payerUserId, category_id = :categoryId, notify = :notify WHERE id = :id"; } } @@ -184,6 +185,7 @@ $stmt->bindParam(':paymentMethodId', $paymentMethodId, SQLITE3_INTEGER); $stmt->bindParam(':payerUserId', $payerUserId, SQLITE3_INTEGER); $stmt->bindParam(':categoryId', $categoryId, SQLITE3_INTEGER); + $stmt->bindParam(':notify', $notify, SQLITE3_INTEGER); if ($stmt->execute()) { $success['status'] = "Success"; diff --git a/endpoints/subscription/get.php b/endpoints/subscription/get.php index 08b3938..c0f29aa 100644 --- a/endpoints/subscription/get.php +++ b/endpoints/subscription/get.php @@ -24,6 +24,7 @@ $subscriptionData['payment_method_id'] = $row['payment_method_id']; $subscriptionData['payer_user_id'] = $row['payer_user_id']; $subscriptionData['category_id'] = $row['category_id']; + $subscriptionData['notify'] = $row['notify']; $subscriptionJson = json_encode($subscriptionData); header('Content-Type: application/json'); diff --git a/index.php b/index.php index cba6db8..041923f 100644 --- a/index.php +++ b/index.php @@ -26,6 +26,16 @@ } } + $notificationsEnabled = false; + $query = "SELECT enabled FROM notifications WHERE id = 1"; + $result = $db->query($query); + if ($result) { + $row = $result->fetchArray(SQLITE3_ASSOC); + if ($row) { + $notificationsEnabled = $row['enabled']; + } + } + $headerClass = count($subscriptions) > 0 ? "main-actions" : "main-actions hidden"; $defaultLogo = $theme == "light" ? "images/wallos.png" : "images/walloswhite.png"; ?> @@ -229,6 +239,17 @@ + +