Added Other AP
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
<IfModule mod_rewrite.c>
|
||||
|
||||
RewriteEngine On
|
||||
RewriteBase /SAVVY/email/
|
||||
#RewriteBase /
|
||||
|
||||
#Checks to
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteRule ^(.*)$ index.php?/$1 [L]
|
||||
|
||||
</IfModule>
|
||||
|
||||
<IfModule !mod_rewrite.c>
|
||||
# If we don't have mod_rewrite installed, all 404's
|
||||
# can be sent to index.php, and everything works as normal.
|
||||
# Submitted by: ElliotHaughin
|
||||
|
||||
ErrorDocument 404 /index.php
|
||||
|
||||
</IfModule>
|
||||
|
||||
#Header add Access-Control-Allow-Origin "*"
|
||||
#Header add Access-Control-Expose-Headers "Access-Control-Allow-Origin"
|
||||
#Header add Access-Control-Allow-Headers "Cache-Control, Pragma, Origin, Authorization, Content-Type, X-Requested-With"
|
||||
#Header add Access-Control-Allow-Methods "POST, GET, PUT, DELETE, OPTIONS"
|
||||
#Header add Content-type "application/json"
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
<?php
|
||||
|
||||
class Email {
|
||||
|
||||
static function createEmail($db, $to, $subject, $htmlBody) {
|
||||
$result = array();
|
||||
|
||||
$status = '0';
|
||||
|
||||
$values = array(
|
||||
"to_emails" => NULL,
|
||||
"subject" => NULL,
|
||||
"html_body" => NULL,
|
||||
"status"=>"0",
|
||||
"created_at"=>(new DateTime())->format('c'),
|
||||
"updated_at"=>(new DateTime())->format('c')
|
||||
);
|
||||
|
||||
if(isset($to)) {
|
||||
$values["to_emails"] = Email::prepareEmailsQueryString($to);
|
||||
}
|
||||
|
||||
if(isset($subject)) {
|
||||
$values["subject"] = pg_escape_string($subject);
|
||||
}
|
||||
|
||||
if(isset($htmlBody)) {
|
||||
$values["html_body"] = pg_escape_string($htmlBody);
|
||||
}
|
||||
|
||||
$values = array_filter($values, 'strlen');
|
||||
|
||||
$q = "INSERT INTO emails";
|
||||
|
||||
// implode keys of $values...
|
||||
$q .= " (".implode(", ", array_keys($values)).")";
|
||||
|
||||
// implode values of $values...
|
||||
$q .= " VALUES ('".implode("', '", $values)."') ";
|
||||
|
||||
$rs = pg_query($db, $q);
|
||||
|
||||
if (!$rs) {
|
||||
return array("success" => false, "message" => "Something went wrong!");
|
||||
exit;
|
||||
}
|
||||
return array("success" => (boolean)pg_affected_rows($rs), "message" => "Successfully create new email.");
|
||||
}
|
||||
|
||||
static function getEmailById($db, $id) {
|
||||
$result = [];
|
||||
$q = "SELECT * from emails where id = '" . pg_escape_string($id) . "'";
|
||||
|
||||
$rs = pg_query($db, $q);
|
||||
|
||||
while ($row = pg_fetch_assoc($rs)) {
|
||||
array_push($result, Email::parseData($row));
|
||||
}
|
||||
|
||||
return array("success" => true, "data" => $result[0]);
|
||||
}
|
||||
|
||||
static function findEmails($db, $filter, $limit=0)
|
||||
{
|
||||
$results = array();
|
||||
$condition = [];
|
||||
$q = "SELECT * FROM emails";
|
||||
|
||||
if (count($filter)) {
|
||||
$q .= " where ";
|
||||
if (isset($filter['status'])) {
|
||||
$condition[] = "status = '" . pg_escape_string($filter['status']) . "'";
|
||||
}
|
||||
if (isset($filter['retries'])) {
|
||||
$condition[] = "retries < ".((int)$filter['retries']);
|
||||
}
|
||||
}
|
||||
|
||||
$q .= implode(" AND ", $condition);
|
||||
|
||||
if ($limit>0) {
|
||||
$q.= " LIMIT ".$limit;
|
||||
}
|
||||
|
||||
$rs = pg_query($db, $q);
|
||||
|
||||
while ($row = pg_fetch_assoc($rs)) {
|
||||
array_push($results, Email::parseData($row));
|
||||
}
|
||||
|
||||
return $results ?? NULL;
|
||||
}
|
||||
|
||||
public static function updateEmail($db, $id, $to, $subject, $htmlBody, $status) {
|
||||
if (!isset($id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$q = "update emails set ";
|
||||
$values = [
|
||||
"updated_at = '" . (new DateTime())->format('c') ."'"
|
||||
];
|
||||
|
||||
if (isset($to)) {
|
||||
// $q .= "to_email = " . Email::prepareEmailsQueryString($to);
|
||||
$values[] = "to_emails = '" . Email::prepareEmailsQueryString($to) . "'";
|
||||
}
|
||||
|
||||
if (isset($subject)) {
|
||||
$values[] = "subject = '" . pg_escape_string($subject) . "'";
|
||||
}
|
||||
|
||||
if (isset($htmlBody)) {
|
||||
$values[] = "html_body = '" . pg_escape_string($htmlBody) . "'";
|
||||
}
|
||||
|
||||
if (isset($status)) {
|
||||
$values[] = "status = '" . pg_escape_string($status) . "'";
|
||||
}
|
||||
|
||||
$q .= implode(", ", $values);
|
||||
$q .= " where id = " . pg_escape_string((string)$id);
|
||||
|
||||
$r = pg_query($db, $q);
|
||||
|
||||
if (!$r) {
|
||||
return array("success" => false, "message" => "Something went wrong!");
|
||||
exit;
|
||||
}
|
||||
|
||||
return array("success" => true, "message" => "Successfully update email.");
|
||||
}
|
||||
|
||||
private static function parseData($obj) {
|
||||
$emails = $obj['to_emails'];
|
||||
$emails = str_replace('{', '', $emails);
|
||||
$emails = str_replace('}', '', $emails);
|
||||
|
||||
$emails = explode(',', $emails);
|
||||
|
||||
$obj['to_emails'] = $emails;
|
||||
|
||||
return $obj;
|
||||
}
|
||||
|
||||
private static function prepareEmailsQueryString($to) {
|
||||
$result = "{";
|
||||
$result .= join(',', $to);
|
||||
$result .= "}";
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
require './vendor/autoload.php';
|
||||
require '../../core/backend.php';
|
||||
|
||||
class EmailApi extends Api
|
||||
{
|
||||
public $apiName = 'email';
|
||||
public $sg;
|
||||
|
||||
public function __construct($requestUri, $encryption=true) {
|
||||
parent::__construct($requestUri, $encryption);
|
||||
|
||||
global $savvyext;
|
||||
|
||||
$apiKey = $savvyext->cfgReadChar('mailsend.api_key');
|
||||
$sg = new \SendGrid($apiKey);
|
||||
|
||||
$this->sg = $sg;
|
||||
}
|
||||
|
||||
public function indexAction() {
|
||||
$results = $this->findNewEmails();
|
||||
return $this->response(
|
||||
array(
|
||||
"success" => true,
|
||||
"data" => $results
|
||||
), 200 );
|
||||
}
|
||||
|
||||
/**
|
||||
* @OA\Post(
|
||||
* path="/v1/email/api/email/new",
|
||||
* summary="Send email to users",
|
||||
* @OA\Parameter(
|
||||
* name="name",
|
||||
* description = "Name of email template",
|
||||
* in="body",
|
||||
* required=true,
|
||||
* @OA\Schema(ref="#/components/schemas/create_params")
|
||||
* )
|
||||
* )
|
||||
*/
|
||||
public function createAction()
|
||||
{
|
||||
$db = new Db();
|
||||
$message = "Failed to send email";
|
||||
|
||||
$subject = $this->requestParams['subject'] ?? NULL;
|
||||
$receivers = $this->requestParams["to"] ?? NULL;
|
||||
$htmlBody = $this->requestParams['html_body'] ?? NULL;
|
||||
|
||||
if (!isset($subject) || !isset($receivers) || !isset($htmlBody)) {
|
||||
return $this->response(
|
||||
array(
|
||||
"success" => false,
|
||||
"message" => 'Missing params'
|
||||
), 400);
|
||||
}
|
||||
|
||||
$result = Email::createEmail(
|
||||
$db->getConnect(),
|
||||
$receivers,
|
||||
$subject,
|
||||
$htmlBody
|
||||
);
|
||||
|
||||
if (!isset($result)) {
|
||||
return $this->response(
|
||||
array(
|
||||
"success" => false,
|
||||
"message" => $message
|
||||
), 500);
|
||||
}
|
||||
|
||||
return $this->response($result, 200);
|
||||
}
|
||||
|
||||
public function viewAction() {}
|
||||
|
||||
public function updateAction() {}
|
||||
|
||||
public function deleteAction() {}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
require './vendor/autoload.php';
|
||||
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Exception\ClientException;
|
||||
use GuzzleHttp\Exception\ServerException;
|
||||
|
||||
class MailchimpAPI
|
||||
{
|
||||
private static function handleResponse($response)
|
||||
{
|
||||
$res = json_decode($response->getBody());
|
||||
|
||||
if (!isset($res)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return array_merge((array) $res);
|
||||
}
|
||||
|
||||
public static function get($url, $params)
|
||||
{
|
||||
require_once('../../core/backend.php');
|
||||
global $savvyext;
|
||||
$token = $savvyext->cfgReadChar('mailchimp.api_key');
|
||||
$baseUri = $savvyext->cfgReadChar('mailchimp.domain');
|
||||
|
||||
$client = new Client(['base_uri' => $baseUri]);
|
||||
$header = ['Authorization' => "apikey $token"];
|
||||
|
||||
try {
|
||||
$response = $client->request('GET', $url, [
|
||||
'headers' => $header,
|
||||
'query' => $params,
|
||||
]);
|
||||
} catch (ServerException $e) {
|
||||
$response = $e->getResponse();
|
||||
} catch (ClientException $e) {
|
||||
$response = $e->getResponse();
|
||||
}
|
||||
|
||||
return self::handleResponse($response);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
require './vendor/autoload.php';
|
||||
|
||||
use \SendGrid\Mail\To;
|
||||
use \SendGrid\Mail\Mail;
|
||||
use \SendGrid\Mail\From;
|
||||
use \SendGrid\Mail\HtmlContent;
|
||||
use \SendGrid\Mail\Personalization;
|
||||
|
||||
class SendGridService {
|
||||
public static function sendEmail($email) {
|
||||
global $savvyext;
|
||||
|
||||
$apiKey = $savvyext->cfgReadChar('mailsend.api_key');
|
||||
$sg = new \SendGrid($apiKey);
|
||||
|
||||
$response = $sg->send($email);
|
||||
return $response;
|
||||
}
|
||||
|
||||
public static function prepareSengridToObject($receiver = [], $substitutions = NULL) {
|
||||
return new \SendGrid\Mail\To(
|
||||
$receiver['email'],
|
||||
$receiver['firstname'] ?? '',
|
||||
$substitutions ?? NULL
|
||||
);
|
||||
}
|
||||
|
||||
public static function prepareEmail($receivers, $subject, $template, $globalSubstitutions) {
|
||||
global $savvyext;
|
||||
|
||||
$fromEmail = $savvyext->cfgReadChar('mailsend.from');
|
||||
$fromName = $savvyext->cfgReadChar('mailsend.name');
|
||||
|
||||
$email = new Mail(
|
||||
new From($fromEmail, $fromName),
|
||||
NULL,
|
||||
$subject, // or array of subjects, these take precendence
|
||||
$plainTextContent ?? NULL,
|
||||
new HtmlContent($template),
|
||||
$globalSubstitutions ?? NULL
|
||||
);
|
||||
|
||||
foreach($receivers as $receiver) {
|
||||
$personalization = SendGridService::createPersonalize($receiver);
|
||||
$email->addPersonalization($personalization);
|
||||
}
|
||||
|
||||
return $email;
|
||||
}
|
||||
|
||||
public static function createPersonalize($to) {
|
||||
$personalize = new Personalization();
|
||||
$personalize->addTo($to);
|
||||
|
||||
return $personalize;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "vendor/email",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Jack",
|
||||
"email": "jack@goldenowl.asia"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"guzzlehttp/guzzle": "~6.0",
|
||||
"sendgrid/sendgrid": "~7"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
<?php
|
||||
|
||||
require_once('../../core/backend.php');
|
||||
|
||||
require_once('../common/Api.php');
|
||||
require_once('../common/Db.php');
|
||||
|
||||
require_once('Email.php');
|
||||
require_once('EmailApi.php');
|
||||
|
||||
$httpAuthToken = $savvyext->cfgReadChar('system.oauth2_token');
|
||||
|
||||
header("Access-Control-Allow-Origin: *");
|
||||
header("Access-Control-Expose-Headers: Access-Control-Allow-Origin");
|
||||
header("Access-Control-Allow-Headers: Cache-Control, Pragma, Origin, Authorization, Content-Type, X-Requested-With, client_id");
|
||||
header("Access-Control-Allow-Methods: POST, GET, PUT, DELETE, OPTIONS");
|
||||
header('Content-type: application/json');
|
||||
|
||||
if ("OPTIONS" === $_SERVER['REQUEST_METHOD']) {
|
||||
exit();
|
||||
}
|
||||
|
||||
$headers = getallheaders();
|
||||
if ((!isset($headers["Authorization"]) || substr($headers["Authorization"],-strlen($httpAuthToken))!=$httpAuthToken) &&
|
||||
(!isset($headers["authorization"]) || substr($headers["authorization"],-strlen($httpAuthToken))!=$httpAuthToken)) {
|
||||
header('HTTP/1.1 401 Unauthorized');
|
||||
header('Status: 401 Unauthorized');
|
||||
echo "{\"status\":\"Missing authorization\"}";
|
||||
exit();
|
||||
}
|
||||
|
||||
try {
|
||||
if (strpos($_SERVER['REQUEST_URI'], '/api/')===false) {
|
||||
throw new Exception("Invalid API request");
|
||||
}
|
||||
$requestUri = explode('/', trim($_SERVER['REQUEST_URI'], '/'));
|
||||
|
||||
while (array_shift($requestUri) !== 'api') {
|
||||
};
|
||||
|
||||
if ($requestUri[0] == 'email') {
|
||||
$api = new EmailApi($requestUri, false);
|
||||
} else {
|
||||
echo json_encode(Array("message" => 'Invalid API request'));
|
||||
}
|
||||
echo $api->run();
|
||||
}
|
||||
catch (Exception $e) {
|
||||
echo json_encode(Array("message" => $e->getMessage()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @OA\Info(
|
||||
* title="Email Endpoint API",
|
||||
* version="1.0",
|
||||
* @OA\Contact(
|
||||
* email="support@float.sg"
|
||||
* )
|
||||
* )
|
||||
*/
|
||||
|
||||
/**
|
||||
* @OA\Schema(
|
||||
* schema="receivers",
|
||||
* type="object",
|
||||
* @OA\Property(
|
||||
* property="email",
|
||||
* description="Email of receiver",
|
||||
* type="string",
|
||||
* format="string",
|
||||
* example="jack@goldenowl.asia"
|
||||
* ),
|
||||
* @OA\Property(
|
||||
* property="name",
|
||||
* description="Name of receiver",
|
||||
* type="string",
|
||||
* format="string",
|
||||
* example="Jack Nguyen",
|
||||
* ),
|
||||
* @OA\Property(
|
||||
* property="substitutions",
|
||||
* description="Receiver's personal information for email"
|
||||
* type="object"
|
||||
* example="{ "{{key}}": "value" }"
|
||||
* )
|
||||
* )
|
||||
*/
|
||||
|
||||
/**
|
||||
* @OA\Schema(
|
||||
* schema="create_params",
|
||||
* type="object",
|
||||
* @OA\Property(
|
||||
* property="name",
|
||||
* description="Name of email template",
|
||||
* type="string",
|
||||
* format="string",
|
||||
* example="welcome"
|
||||
* ),
|
||||
* @OA\Property(
|
||||
* property="data",
|
||||
* description="Global dynamic data for the template",
|
||||
* type="object",
|
||||
* properties:
|
||||
* key:
|
||||
* type: string,
|
||||
* format: "{{key_name}}"
|
||||
* value:
|
||||
* type: string
|
||||
* example= {
|
||||
* "{{contactus}}": "https://float.sg"
|
||||
* }
|
||||
* ),
|
||||
* @OA\Property(
|
||||
* property="receivers",
|
||||
* description="List email's receivers",
|
||||
* type="array",
|
||||
* items="$ref: '#/components/schemas/receivers' "
|
||||
*
|
||||
* )
|
||||
* )
|
||||
*/
|
||||
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
$profile = getenv("SAVVY_PROFILE");
|
||||
if ($profile) {
|
||||
require("../../core/backend.${profile}.php");
|
||||
} else {
|
||||
require('../../core/backend.php');
|
||||
}
|
||||
|
||||
require_once('../common/Db.php');
|
||||
require('./Email.php');
|
||||
|
||||
global $savvyext;
|
||||
|
||||
$batch_limit = 100;
|
||||
$sleep_interval = 60;
|
||||
$max_retries = 3;
|
||||
|
||||
$db = new Db();
|
||||
|
||||
$client = new GearmanClient();
|
||||
$client->addServers($savvyext->cfgReadChar('gearman.servers'));
|
||||
|
||||
do {
|
||||
echo "[".date("Y-m-d H:i:s")."] Scheduler is starting\n";
|
||||
$i_need_some_sleep = true;
|
||||
|
||||
$q = "UPDATE emails SET status=0, updated_at=NOW() WHERE status = 2 AND updated_at + interval '5 minutes' < now()";
|
||||
pg_query($db->getConnect(), $q);
|
||||
|
||||
$emails = Email::findEmails($db->getConnect(), ['status' => 2], $batch_limit+1);
|
||||
if (is_array($emails) && count($emails) < $batch_limit) {
|
||||
$emails = Email::findEmails($db->getConnect(), ['status' => 0,'retries' => $max_retries], $batch_limit+1 - count($emails));
|
||||
if (is_array($emails) && count($emails)>0) {
|
||||
echo "[".date("Y-m-d H:i:s")."] Scheduling ".count($emails)." email(s)\n";
|
||||
$i = 1; // Base 1 for > condition
|
||||
foreach ($emails as $email) {
|
||||
if ($i>$batch_limit) {
|
||||
$i_need_some_sleep = true;
|
||||
break;
|
||||
}
|
||||
$q = "UPDATE emails SET status=2, updated_at=NOW() WHERE id=".$email['id'];
|
||||
pg_query($db->getConnect(), $q);
|
||||
$client->doBackground('sendemail', json_encode($email));
|
||||
$i++;
|
||||
}
|
||||
$i_need_some_sleep = false;
|
||||
}
|
||||
}
|
||||
|
||||
if ($i_need_some_sleep) {
|
||||
sleep($sleep_interval); // Sleep for 1 minute between cycles
|
||||
}
|
||||
} while(true);
|
||||
@@ -0,0 +1,92 @@
|
||||
<?php
|
||||
|
||||
$profile = getenv("SAVVY_PROFILE");
|
||||
if ($profile) {
|
||||
require("../../core/backend.${profile}.php");
|
||||
} else {
|
||||
require('../../core/backend.php');
|
||||
}
|
||||
|
||||
require './vendor/autoload.php';
|
||||
|
||||
require('./Email.php');
|
||||
require('./Sendgrid.php');
|
||||
require_once('../common/Db.php');
|
||||
|
||||
global $savvyext;
|
||||
|
||||
$db = new Db();
|
||||
|
||||
$apiKey = $savvyext->cfgReadChar('mailsend.api_key');
|
||||
$sg = new \SendGrid($apiKey);
|
||||
|
||||
|
||||
$worker= new GearmanWorker();
|
||||
$worker->addServers($savvyext->cfgReadChar('gearman.servers'));
|
||||
$worker->addFunction('sendemail', 'sendEmailJob');
|
||||
|
||||
while (1)
|
||||
{
|
||||
$worker->work();
|
||||
if ($worker->returnCode() != GEARMAN_SUCCESS) break;
|
||||
}
|
||||
|
||||
function sendEmailJob($job) {
|
||||
global $db, $sg;
|
||||
echo "[".date("Y-m-d H:i:s")."] Send Email job starting\n";
|
||||
|
||||
$workload = $job->workload();
|
||||
$email = json_decode($workload, true);
|
||||
$id = $email['id'];
|
||||
|
||||
echo "[".date("Y-m-d H:i:s")."] Processing email ID #${id}\n";
|
||||
|
||||
// $q = "UPDATE emails SET status=2, updated_at=NOW() WHERE id=${id}";
|
||||
// pg_query($db->getConnect(), $q);
|
||||
|
||||
if (is_array($email) && array_key_exists('to_emails',$email)
|
||||
&& is_array($email['to_emails']) && count($email['to_emails'])>0) {
|
||||
|
||||
$receivers = array_map(function($to) {
|
||||
return ['email' => $to];
|
||||
}, $email['to_emails']);
|
||||
|
||||
// Prepare sendgrid receivers
|
||||
$sendgridReceivers = prepareReceivers($receivers, []);
|
||||
$subject = $email['subject'] ?? '';
|
||||
$template = $email['html_body'] ?? '';
|
||||
|
||||
// Prepare email will be sent through sendgrid
|
||||
$parsedEmail = SendGridService::prepareEmail($sendgridReceivers, $subject, $template, []);
|
||||
|
||||
// Send email
|
||||
try {
|
||||
$response = $sg->send($parsedEmail);
|
||||
//Email::updateEmail($db->getConnect(), $id, NULL, NULL, NULL, '1');
|
||||
if ($response->statusCode()==202) {
|
||||
$q = "UPDATE emails SET status=1,updated_at=NOW(),message=NULL WHERE id=".$id;
|
||||
$r = pg_query($db->getConnect(), $q);
|
||||
} else {
|
||||
throw new Exception("Invalid return status code: ".$response->statusCode());
|
||||
}
|
||||
echo "[".date("Y-m-d H:i:s")."] Email sent successfully.\n";
|
||||
} catch(Exception $e) {
|
||||
// TODO: Handle send email error.
|
||||
// Maybe we can log the error here
|
||||
$msg = pg_escape_string(substr($e->getMessage(),0,200));
|
||||
$q = "UPDATE emails SET status=0, retries=retries+1, updated_at=NOW(),message='${msg}' WHERE id= ${id}";
|
||||
$r = pg_query($db->getConnect(), $q);
|
||||
echo "[".date("Y-m-d H:i:s")."] Email not sent: ${msg}\n";
|
||||
}
|
||||
}
|
||||
echo "[".date("Y-m-d H:i:s")."] Send Email job complete\n";
|
||||
}
|
||||
|
||||
function prepareReceivers($receivers = [], $subs = []) {
|
||||
$results = [];
|
||||
foreach ( $receivers as $key => $receiver ) {
|
||||
$substitutions = array_merge($subs, $receiver['substitutions'] ?? []);
|
||||
$results[] = SendGridService::prepareSengridToObject($receiver, $substitutions ?? []) ?? NULL;
|
||||
}
|
||||
return $results;
|
||||
}
|
||||
Reference in New Issue
Block a user