diff --git a/admin.php b/admin.php index 5f8a686..3aa6e76 100644 --- a/admin.php +++ b/admin.php @@ -29,7 +29,7 @@ $total_records = $total_stmt->fetchColumn(); $total_pages = ceil($total_records / $records_per_page); // Get records for the current page -$stmt = $pdo->prepare("SELECT id, first_name, last_name, email, created_at FROM attendees ORDER BY first_name ASC, last_name ASC LIMIT :limit OFFSET :offset"); +$stmt = $pdo->prepare("SELECT id, first_name, last_name, email, created_at, how_did_you_hear, company FROM attendees ORDER BY first_name ASC, last_name ASC LIMIT :limit OFFSET :offset"); $stmt->bindValue(':limit', $records_per_page, PDO::PARAM_INT); $stmt->bindValue(':offset', $offset, PDO::PARAM_INT); $stmt->execute(); @@ -67,6 +67,8 @@ $attendees = $stmt->fetchAll(PDO::FETCH_ASSOC); First Name Last Name Email + Source + Company Registered At Actions @@ -74,7 +76,7 @@ $attendees = $stmt->fetchAll(PDO::FETCH_ASSOC); - No attendees found. + No attendees found. @@ -83,6 +85,8 @@ $attendees = $stmt->fetchAll(PDO::FETCH_ASSOC); + + Edit diff --git a/describe_table.php b/describe_table.php deleted file mode 100644 index 477ef4a..0000000 --- a/describe_table.php +++ /dev/null @@ -1,8 +0,0 @@ -query('DESCRIBE attendees'); - print_r($stmt->fetchAll(PDO::FETCH_ASSOC)); -} catch (PDOException $e) { - echo 'Error: ' . $e->getMessage(); -} diff --git a/export_csv.php b/export_csv.php index 5384d96..3c0d61e 100644 --- a/export_csv.php +++ b/export_csv.php @@ -10,7 +10,7 @@ if (!isset($_SESSION['user']) || $_SESSION['user'] !== 'admin') { } $pdo = db(); -$stmt = $pdo->query("SELECT first_name, last_name, email FROM attendees ORDER BY created_at DESC"); +$stmt = $pdo->query("SELECT first_name, last_name, email, how_did_you_hear, company FROM attendees ORDER BY created_at DESC"); $attendees = $stmt->fetchAll(PDO::FETCH_ASSOC); header('Content-Type: text/csv; charset=utf-8'); @@ -22,14 +22,16 @@ $output = fopen('php://output', 'w'); fputs($output, "\xEF\xBB\xBF"); // Add header row -fputcsv($output, ['First Name', 'Last Name', 'Email']); +fputcsv($output, ['First Name', 'Last Name', 'Email', 'Source', 'Company']); // Add data rows foreach ($attendees as $attendee) { fputcsv($output, [ $attendee['first_name'], $attendee['last_name'], - $attendee['email'] + $attendee['email'], + $attendee['how_did_you_hear'] ?? '', + $attendee['company'] ?? '' ]); } diff --git a/mail/MailService.php b/mail/MailService.php index d801067..0d65e2f 100644 --- a/mail/MailService.php +++ b/mail/MailService.php @@ -14,35 +14,24 @@ class MailService { $cfg = self::loadConfig(); - $autoload = __DIR__ . '/../vendor/autoload.php'; - if (file_exists($autoload)) { - require_once $autoload; - } - if (!class_exists('PHPMailer\\PHPMailer\\PHPMailer')) { - @require_once 'libphp-phpmailer/autoload.php'; - if (!class_exists('PHPMailer\\PHPMailer\\PHPMailer')) { - @require_once 'libphp-phpmailer/src/Exception.php'; - @require_once 'libphp-phpmailer/src/SMTP.php'; - @require_once 'libphp-phpmailer/src/PHPMailer.php'; - } - if (!class_exists('PHPMailer\\PHPMailer\\PHPMailer')) { - @require_once 'PHPMailer/src/Exception.php'; - @require_once 'PHPMailer/src/SMTP.php'; - @require_once 'PHPMailer/src/PHPMailer.php'; - } - if (!class_exists('PHPMailer\\PHPMailer\\PHPMailer')) { - @require_once 'PHPMailer/Exception.php'; - @require_once 'PHPMailer/SMTP.php'; - @require_once 'PHPMailer/PHPMailer.php'; - } + // Try Composer autoload first, then fall back to the system-wide (apt) PHPMailer. + $composerAutoload = __DIR__ . '/../vendor/autoload.php'; + if (file_exists($composerAutoload)) { + require_once $composerAutoload; + } elseif (file_exists('/usr/share/php/libphp-phpmailer/autoload.php')) { + require_once '/usr/share/php/libphp-phpmailer/autoload.php'; } - if (!class_exists('PHPMailer\\PHPMailer\\PHPMailer')) { + if (!class_exists('PHPMailer\PHPMailer\PHPMailer')) { return [ 'success' => false, 'error' => 'PHPMailer not available' ]; } $mail = new PHPMailer\PHPMailer\PHPMailer(true); try { + $mail->SMTPDebug = 2; + $mail->Debugoutput = function($str, $level) { + file_put_contents(__DIR__ . '/mail.log', $str, FILE_APPEND); + }; $mail->isSMTP(); $mail->Host = $cfg['smtp_host'] ?? ''; $mail->Port = (int)($cfg['smtp_port'] ?? 587); @@ -54,7 +43,7 @@ class MailService $mail->Username = $cfg['smtp_user'] ?? ''; $mail->Password = $cfg['smtp_pass'] ?? ''; - $fromEmail = $opts['from_email'] ?? ($cfg['from_email'] ?? 'no-reply@localhost'); + $fromEmail = $opts['from_email'] ?? ($cfg['from_email'] ?? 'support@flatlogic.com'); $fromName = $opts['from_name'] ?? ($cfg['from_name'] ?? 'App'); $mail->setFrom($fromEmail, $fromName); if (!empty($opts['reply_to']) && filter_var($opts['reply_to'], FILTER_VALIDATE_EMAIL)) { @@ -118,31 +107,12 @@ class MailService { $cfg = self::loadConfig(); - // Try Composer autoload if available (for PHPMailer) - $autoload = __DIR__ . '/../vendor/autoload.php'; - if (file_exists($autoload)) { - require_once $autoload; - } - // Fallback to system-wide PHPMailer (installed via apt: libphp-phpmailer) - if (!class_exists('PHPMailer\\PHPMailer\\PHPMailer')) { - // Debian/Ubuntu package layout (libphp-phpmailer) - @require_once 'libphp-phpmailer/autoload.php'; - if (!class_exists('PHPMailer\\PHPMailer\\PHPMailer')) { - @require_once 'libphp-phpmailer/src/Exception.php'; - @require_once 'libphp-phpmailer/src/SMTP.php'; - @require_once 'libphp-phpmailer/src/PHPMailer.php'; - } - // Alternative layout (older PHPMailer package names) - if (!class_exists('PHPMailer\\PHPMailer\\PHPMailer')) { - @require_once 'PHPMailer/src/Exception.php'; - @require_once 'PHPMailer/src/SMTP.php'; - @require_once 'PHPMailer/src/PHPMailer.php'; - } - if (!class_exists('PHPMailer\\PHPMailer\\PHPMailer')) { - @require_once 'PHPMailer/Exception.php'; - @require_once 'PHPMailer/SMTP.php'; - @require_once 'PHPMailer/PHPMailer.php'; - } + // Try Composer autoload first, then fall back to the system-wide (apt) PHPMailer. + $composerAutoload = __DIR__ . '/../vendor/autoload.php'; + if (file_exists($composerAutoload)) { + require_once $composerAutoload; + } elseif (file_exists('/usr/share/php/libphp-phpmailer/autoload.php')) { + require_once '/usr/share/php/libphp-phpmailer/autoload.php'; } $transport = $cfg['transport'] ?? 'smtp'; @@ -158,6 +128,10 @@ class MailService { $mail = new PHPMailer\PHPMailer\PHPMailer(true); try { + $mail->SMTPDebug = 2; + $mail->Debugoutput = function($str, $level) { + file_put_contents(__DIR__ . '/mail.log', $str, FILE_APPEND); + }; $mail->isSMTP(); $mail->Host = $cfg['smtp_host'] ?? ''; $mail->Port = (int)($cfg['smtp_port'] ?? 587); @@ -169,7 +143,7 @@ class MailService $mail->Username = $cfg['smtp_user'] ?? ''; $mail->Password = $cfg['smtp_pass'] ?? ''; - $fromEmail = $cfg['from_email'] ?? 'no-reply@localhost'; + $fromEmail = $cfg['from_email'] ?? 'support@flatlogic.com'; $fromName = $cfg['from_name'] ?? 'App'; $mail->setFrom($fromEmail, $fromName); diff --git a/mail/config.php b/mail/config.php index 626cca1..7bc9bce 100644 --- a/mail/config.php +++ b/mail/config.php @@ -40,15 +40,15 @@ load_dotenv_if_needed([ ]); $transport = env_val('MAIL_TRANSPORT', 'smtp'); -$smtp_host = env_val('SMTP_HOST'); +$smtp_host = env_val('SMTP_HOST', 'email-smtp.us-east-1.amazonaws.com'); $smtp_port = (int) env_val('SMTP_PORT', 587); $smtp_secure = env_val('SMTP_SECURE', 'tls'); // tls | ssl | null -$smtp_user = env_val('SMTP_USER'); -$smtp_pass = env_val('SMTP_PASS'); +$smtp_user = env_val('SMTP_USER', 'AKIAVEW7G4PQUBGM52OF'); +$smtp_pass = env_val('SMTP_PASS', 'BLnD4hKGb6YkSz3gaQrf8fnyLi3C3/EdjOOsLEDTDPTz'); -$from_email = env_val('MAIL_FROM', 'no-reply@localhost'); -$from_name = env_val('MAIL_FROM_NAME', 'App'); -$reply_to = env_val('MAIL_REPLY_TO'); +$from_email = env_val('MAIL_FROM', 'app@flatlogic.app'); +$from_name = env_val('MAIL_FROM_NAME', 'Flatlogic App'); +$reply_to = env_val('MAIL_REPLY_TO', 'app@flatlogic.app'); $dkim_domain = env_val('DKIM_DOMAIN'); $dkim_selector = env_val('DKIM_SELECTOR'); diff --git a/mail/mail.log b/mail/mail.log new file mode 100644 index 0000000..66a55a5 --- /dev/null +++ b/mail/mail.log @@ -0,0 +1,195 @@ +SERVER -> CLIENT: 220 email-smtp.amazonaws.com ESMTP SimpleEmailService-d-5KYUAZVUF HYrGkiakGqXZYkI8moXl +CLIENT -> SERVER: EHLO webinar-registration.dev.flatlogic.app +SERVER -> CLIENT: 250-email-smtp.amazonaws.com +250-8BITMIME +250-STARTTLS +250-AUTH PLAIN LOGIN +250 Ok +CLIENT -> SERVER: STARTTLS +SERVER -> CLIENT: 220 Ready to start TLS +CLIENT -> SERVER: EHLO webinar-registration.dev.flatlogic.app +SERVER -> CLIENT: 250-email-smtp.amazonaws.com +250-8BITMIME +250-STARTTLS +250-AUTH PLAIN LOGIN +250 Ok +CLIENT -> SERVER: AUTH LOGIN +SERVER -> CLIENT: 334 VXNlcm5hbWU6 +CLIENT -> SERVER: [credentials hidden]SERVER -> CLIENT: 334 UGFzc3dvcmQ6 +CLIENT -> SERVER: [credentials hidden]SERVER -> CLIENT: 235 Authentication successful. +CLIENT -> SERVER: MAIL FROM: +SERVER -> CLIENT: 250 Ok +CLIENT -> SERVER: RCPT TO: +SERVER -> CLIENT: 250 Ok +CLIENT -> SERVER: DATA +SERVER -> CLIENT: 354 End data with . +CLIENT -> SERVER: Date: Fri, 31 Oct 2025 10:33:06 +0000 +CLIENT -> SERVER: To: Blarior@gmail.com +CLIENT -> SERVER: From: Flatlogic App +CLIENT -> SERVER: Reply-To: app@flatlogic.app +CLIENT -> SERVER: Subject: Confirmation: You're Registered for Building Scalable Apps with AppWizzy +CLIENT -> SERVER: Message-ID: +CLIENT -> SERVER: X-Mailer: PHPMailer 6.6.3 (https://github.com/PHPMailer/PHPMailer) +CLIENT -> SERVER: MIME-Version: 1.0 +CLIENT -> SERVER: Content-Type: multipart/alternative; +CLIENT -> SERVER: boundary="b1_b2vWqyHUE69uA6oPGIGO4Am9Ilj5uKn0OhRAVGcAg" +CLIENT -> SERVER: Content-Transfer-Encoding: 8bit +CLIENT -> SERVER: +CLIENT -> SERVER: This is a multi-part message in MIME format. +CLIENT -> SERVER: +CLIENT -> SERVER: --b1_b2vWqyHUE69uA6oPGIGO4Am9Ilj5uKn0OhRAVGcAg +CLIENT -> SERVER: Content-Type: text/plain; charset=iso-8859-1 +CLIENT -> SERVER: Content-Transfer-Encoding: 8bit +CLIENT -> SERVER: +CLIENT -> SERVER: +CLIENT -> SERVER: +CLIENT -> SERVER: +CLIENT -> SERVER: +CLIENT -> SERVER: +CLIENT -> SERVER: Webinar Registration Confirmation +CLIENT -> SERVER: +CLIENT -> SERVER: +CLIENT -> SERVER: +CLIENT -> SERVER: +CLIENT -> SERVER: +CLIENT -> SERVER: +CLIENT -> SERVER: +CLIENT -> SERVER: +CLIENT -> SERVER: +CLIENT -> SERVER: +CLIENT -> SERVER: You're Registered! +CLIENT -> SERVER: +CLIENT -> SERVER: +CLIENT -> SERVER: +CLIENT -> SERVER: +CLIENT -> SERVER: +CLIENT -> SERVER: Hello Почтовый, +CLIENT -> SERVER: Thank you for registering for the Building Scalable Apps with AppWizzy webinar. +CLIENT -> SERVER: We're excited to have you join us for this professional vibe-coding session. +CLIENT -> SERVER: +CLIENT -> SERVER: Webinar Details: +CLIENT -> SERVER: Wednesday, November 19, 2025 | 10:00 AM EST +CLIENT -> SERVER: +CLIENT -> SERVER: You'll learn the fastest way to go from an idea to a working app you own, running on your server, with your database, using real frameworks. +CLIENT -> SERVER: +CLIENT -> SERVER: Access Your Dashboard +CLIENT -> SERVER: +CLIENT -> SERVER: +CLIENT -> SERVER: +CLIENT -> SERVER: +CLIENT -> SERVER: +CLIENT -> SERVER: +CLIENT -> SERVER: Meet the Speakers +CLIENT -> SERVER: +CLIENT -> SERVER: +CLIENT -> SERVER: +CLIENT -> SERVER: Philip Daineka +CLIENT -> SERVER: CEO, AppWizzy +CLIENT -> SERVER: +CLIENT -> SERVER: +CLIENT -> SERVER: Alexandr Rubanau +CLIENT -> SERVER: Lead Engineer +CLIENT -> SERVER: +CLIENT -> SERVER: +CLIENT -> SERVER: Alexey Vertel +CLIENT -> SERVER: Lead Engineer +CLIENT -> SERVER: +CLIENT -> SERVER: +CLIENT -> SERVER: +CLIENT -> SERVER: +CLIENT -> SERVER: +CLIENT -> SERVER: +CLIENT -> SERVER: +CLIENT -> SERVER: +CLIENT -> SERVER: © 2025 AppWizzy. All rights reserved. +CLIENT -> SERVER: You can visit our website for more information. +CLIENT -> SERVER: +CLIENT -> SERVER: +CLIENT -> SERVER: +CLIENT -> SERVER: +CLIENT -> SERVER: +CLIENT -> SERVER: +CLIENT -> SERVER: +CLIENT -> SERVER: +CLIENT -> SERVER: --b1_b2vWqyHUE69uA6oPGIGO4Am9Ilj5uKn0OhRAVGcAg +CLIENT -> SERVER: Content-Type: text/html; charset=iso-8859-1 +CLIENT -> SERVER: Content-Transfer-Encoding: 8bit +CLIENT -> SERVER: +CLIENT -> SERVER: +CLIENT -> SERVER: +CLIENT -> SERVER: +CLIENT -> SERVER: +CLIENT -> SERVER: +CLIENT -> SERVER: Webinar Registration Confirmation +CLIENT -> SERVER: +CLIENT -> SERVER: +CLIENT -> SERVER: +CLIENT -> SERVER: +CLIENT -> SERVER: +CLIENT -> SERVER: +CLIENT -> SERVER:
+CLIENT -> SERVER: +CLIENT -> SERVER: +CLIENT -> SERVER: +CLIENT -> SERVER: +CLIENT -> SERVER: +CLIENT -> SERVER: +CLIENT -> SERVER: +CLIENT -> SERVER: +CLIENT -> SERVER: +CLIENT -> SERVER: +CLIENT -> SERVER: +CLIENT -> SERVER: +CLIENT -> SERVER: +CLIENT -> SERVER: +CLIENT -> SERVER: +CLIENT -> SERVER: +CLIENT -> SERVER: +CLIENT -> SERVER:
+CLIENT -> SERVER: AppWizzy Logo +CLIENT -> SERVER:

You're Registered!

+CLIENT -> SERVER:
+CLIENT -> SERVER:

Hello Почтовый,

+CLIENT -> SERVER:

Thank you for registering for the Building Scalable Apps with AppWizzy webinar.

+CLIENT -> SERVER:

We're excited to have you join us for this professional vibe-coding session.

+CLIENT -> SERVER:
+CLIENT -> SERVER:

Webinar Details:

+CLIENT -> SERVER:

Wednesday, November 19, 2025 | 10:00 AM EST

+CLIENT -> SERVER:
+CLIENT -> SERVER:

You'll learn the fastest way to go from an idea to a working app you own, running on your server, with your database, using real frameworks.

+CLIENT -> SERVER:

+CLIENT -> SERVER: Access Your Dashboard +CLIENT -> SERVER:

+CLIENT -> SERVER:
+CLIENT -> SERVER:

Meet the Speakers

+CLIENT -> SERVER: +CLIENT -> SERVER: +CLIENT -> SERVER: +CLIENT -> SERVER: +CLIENT -> SERVER: +CLIENT -> SERVER: +CLIENT -> SERVER:
+CLIENT -> SERVER:

Philip Daineka

+CLIENT -> SERVER:

CEO, AppWizzy

+CLIENT -> SERVER:
+CLIENT -> SERVER:

Alexandr Rubanau

+CLIENT -> SERVER:

Lead Engineer

+CLIENT -> SERVER:
+CLIENT -> SERVER:

Alexey Vertel

+CLIENT -> SERVER:

Lead Engineer

+CLIENT -> SERVER:
+CLIENT -> SERVER:
+CLIENT -> SERVER:

© 2025 AppWizzy. All rights reserved.

+CLIENT -> SERVER:

You can visit our website for more information.

+CLIENT -> SERVER:
+CLIENT -> SERVER:
+CLIENT -> SERVER: +CLIENT -> SERVER: +CLIENT -> SERVER: +CLIENT -> SERVER: +CLIENT -> SERVER: --b1_b2vWqyHUE69uA6oPGIGO4Am9Ilj5uKn0OhRAVGcAg-- +CLIENT -> SERVER: +CLIENT -> SERVER: . +SERVER -> CLIENT: 250 Ok 0100019a39d403bd-4dd3eb82-372d-4d1c-9605-2624e55d57fc-000000 +CLIENT -> SERVER: QUIT +SERVER -> CLIENT: 221 Bye diff --git a/register.php b/register.php index 6ce79f3..cd773b5 100644 --- a/register.php +++ b/register.php @@ -48,17 +48,20 @@ if (!$first_name || !$last_name || !$email) { } try { - // --- CHECK IF ALREADY REGISTERED OR SOFT-DELETED -- - + // --- CHECK IF ALREADY REGISTERED OR SOFT-DELETED -- - $stmt = db()->prepare("SELECT id, deleted_at FROM attendees WHERE webinar_id = ? AND email = ?"); $stmt->execute([$webinar_id, $email]); $existing_user = $stmt->fetch(PDO::FETCH_ASSOC); + $send_email = false; + if ($existing_user) { if ($existing_user['deleted_at'] !== null) { // --- USER IS SOFT-DELETED, SO REACTIVATE AND UPDATE --- $sql = "UPDATE attendees SET first_name = ?, last_name = ?, company = ?, how_did_you_hear = ?, timezone = ?, deleted_at = NULL, consented = 1 WHERE id = ?"; $stmt = db()->prepare($sql); $stmt->execute([$first_name, $last_name, $company, $how_did_you_hear, $timezone, $existing_user['id']]); + $send_email = true; } else { // --- USER IS ACTIVE, SO REJECT --- echo json_encode(['success' => false, 'error' => 'You are already registered for this webinar.']); @@ -71,16 +74,18 @@ try { VALUES (?, ?, ?, ?, ?, ?, ?, ?)"; $stmt = db()->prepare($sql); $stmt->execute([$webinar_id, $first_name, $last_name, $email, $company, $how_did_you_hear, $password_hash, $timezone]); + $send_email = true; } - // --- SEND CONFIRMATION EMAIL --- - $protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http'; - $host = $_SERVER['HTTP_HOST']; - $logo_url = $protocol . '://' . $host . '/assets/pasted-20251030-095744-1b7c02ab.png'; - $subject = "Confirmation: You're Registered for Building Scalable Apps with AppWizzy"; - $webinar_date_obj = new DateTime('2025-11-19 10:00:00', new DateTimeZone('America/New_York')); + if ($send_email) { + // --- SEND CONFIRMATION EMAIL --- + $protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http'; + $host = $_SERVER['HTTP_HOST']; + $logo_url = $protocol . '://' . $host . '/assets/pasted-20251030-095744-1b7c02ab.png'; + $subject = "Confirmation: You're Registered for Building Scalable Apps with AppWizzy"; + $webinar_date_obj = new DateTime('2025-11-19 10:00:00', new DateTimeZone('America/New_York')); - $body_html = << @@ -152,14 +157,15 @@ try { HTML; - MailService::sendMail($email, $subject, $body_html); + MailService::sendMail($email, $subject, $body_html); + } // --- PREPARE SUCCESS RESPONSE --- $webinar_date = new DateTime('2025-11-19 10:00:00', new DateTimeZone('America/New_York')); $webinar_date->setTimezone(new DateTimeZone('UTC')); - $start_time_utc = $webinar_date->format('Ymd\THis\Z'); + $start_time_utc = $webinar_date->format('Ymd H:i:s Z'); $webinar_date->add(new DateInterval('PT1H')); // Assume 1 hour duration - $end_time_utc = $webinar_date->format('Ymd\THis\Z'); + $end_time_utc = $webinar_date->format('Ymd H:i:s Z'); $event_title = 'Building Scalable Apps with AppWizzy at 4PM CET'; $event_description = 'Professional Vibe-Coding Webinar\n\nJoin us for this webinar at 4PM CET | 10AM EST | 7AM PST. The fastest way to go from an idea to a working app you own, running on your server, with your database, using real frameworks.'; @@ -189,8 +195,8 @@ HTML; 'outlook_link' => $outlook_link ]); -} catch (Exception $e) { - error_log("Registration error: " . $e->getMessage()); +} + catch (Exception $e) { http_response_code(500); echo json_encode(['success' => false, 'error' => 'An unexpected server error occurred. Please try again.']); }