security Posted December 31, 2025 at 04:11 PM Report #635497 Posted December 31, 2025 at 04:11 PM Ola a todos, Tenho um sistema que estou a desenvolver e preciso que envie email sempre que existe um registo de um novo master user ou então quando é Solicitado o reenvio do email de ativação de conta, que acontece quando é definida a password, atraves de um email que contem um link (email convite)! Pois bem, agora vamos entrar no pior, no sistema o master user ativo (OWNER ou SUPER_ADMIN) podem criar regras de notificação Selecionando: - Evento (NEW_MASTER_USER, INVITE_MASTER_USER, LOGIN_BLOCKED, PASSWORD_RESET, SECURITY_ALERT, SYSTEM_ERROR) - Tipo (Utilizador, Perfil (Role), Email Externo) - Destino (Depende do tipo - Tipo: Utilizador - Destino: Seleciona Master User Ativo num <select> - Tipo: Perfil (Role) - Destino: OWNER, SUPER_ADMIN, ADMIN, FINANCEIRO (uma das opçoes) - Tipo: Email externo - Destino: Email a escolha escrito pelo utilizador Acontece que o sistema nao envia os emails devidos e da a informação que foi Reenviado! Abaixo vou colocar o código dos ficheiros com o respetivo caminho que acho que são relevantes para ver se alguem me consegue ajudar... config/config.php <?php declare(strict_types=1); date_default_timezone_set('UTC'); /* ================= ENV ================= */ function env(string $key, $default = null) { static $vars = null; if ($vars === null) { $vars = []; $path = __DIR__ . '/../.env'; if (file_exists($path)) { foreach (file($path, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES) as $line) { if (str_starts_with(trim($line), '#')) continue; [$k, $v] = explode('=', $line, 2); $vars[$k] = trim($v, "\"'"); } } } return $vars[$key] ?? $default; } /* ================= MAIL ================= */ define('MAIL_ENABLED', env('MAIL_ENABLED', 'false') === 'true'); define('MAIL_HOST', env('MAIL_HOST')); define('MAIL_PORT', (int)env('MAIL_PORT', 587)); define('MAIL_ENCRYPTION', env('MAIL_ENCRYPTION', 'tls')); define('MAIL_USERNAME', env('MAIL_USERNAME')); define('MAIL_PASSWORD', env('MAIL_PASSWORD')); define('MAIL_FROM_EMAIL', env('MAIL_FROM_EMAIL')); define('MAIL_FROM_NAME', env('MAIL_FROM_NAME')); /* ================= SESSION ================= */ if (session_status() !== PHP_SESSION_ACTIVE) { session_start(); } /* ================= PDO ================= */ require_once __DIR__ . '/database.php'; /* ================= CORE ================= */ require_once __DIR__ . '/functions.php'; require_once __DIR__ . '/security.php'; /* ================= SERVICES ================= */ require_once __DIR__ . '/../services/EmailLogger.php'; require_once __DIR__ . '/../services/EmailService.php'; define('BASE_URL', 'https://fleetlicenser.mabpinto.ddns.net'); /services/EmailService.php <?php declare(strict_types=1); use PHPMailer\PHPMailer\PHPMailer; require_once __DIR__ . '/../vendor/autoload.php'; final class EmailService { public static function send( string $eventKey, array $variables = [], array $context = [] ): void { if (!MAIL_ENABLED) { return; } global $pdo; if (!$pdo instanceof PDO) { error_log('[EMAIL] PDO indisponível'); return; } /* EVENTO */ $stmt = $pdo->prepare(" SELECT id_event FROM tb_email_event WHERE event_key = ? AND ativo = 1 "); $stmt->execute([$eventKey]); if (!$stmt->fetchColumn()) { error_log("[EMAIL] Evento inválido: {$eventKey}"); return; } /* TEMPLATE */ $stmt = $pdo->prepare(" SELECT subject, body_html, body_text FROM tb_email_template WHERE event_key = ? AND active = 1 LIMIT 1 "); $stmt->execute([$eventKey]); $tpl = $stmt->fetch(PDO::FETCH_ASSOC); if (!$tpl) { error_log("[EMAIL] Template inexistente: {$eventKey}"); return; } /* RECIPIENTS */ $stmt = $pdo->prepare(" SELECT r.recipient_type, r.recipient_value FROM tb_email_recipient r JOIN tb_email_event e ON e.id_event = r.id_event WHERE e.event_key = ? AND r.enabled = 1 AND e.ativo = 1 "); $stmt->execute([$eventKey]); $emails = []; foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $r) { if ($r['recipient_type'] === 'EMAIL') { $emails[] = $r['recipient_value']; } } if (!$emails) { error_log("[EMAIL] Sem destinatários: {$eventKey}"); return; } /* SMTP */ try { $mail = new PHPMailer(true); $mail->isSMTP(); $mail->Host = MAIL_HOST; $mail->SMTPAuth = true; $mail->Username = MAIL_USERNAME; $mail->Password = MAIL_PASSWORD; $mail->Port = MAIL_PORT; $mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS; $mail->setFrom(MAIL_FROM_EMAIL, MAIL_FROM_NAME); foreach ($emails as $e) { $mail->addAddress($e); } $mail->isHTML(true); $mail->Subject = self::parse($tpl['subject'], $variables); $mail->Body = self::parse($tpl['body_html'], $variables); $mail->AltBody = strip_tags($mail->Body); $mail->send(); EmailLogger::log($pdo, $eventKey, $emails, 'SENT', null, $context); } catch (Throwable $e) { EmailLogger::log($pdo, $eventKey, $emails, 'ERROR', $e->getMessage(), $context); error_log('[EMAIL ERROR] ' . $e->getMessage()); } } private static function parse(string $text, array $vars): string { foreach ($vars as $k => $v) { $text = str_replace('{{'.$k.'}}', (string)$v, $text); } return $text; } } /services/EmailLogger.php <?php declare(strict_types=1); class EmailLogger { public static function log( PDO $pdo, string $eventKey, array $recipients, string $status, ?string $error = null, array $context = [] ): void { $stmt = $pdo->prepare(" INSERT INTO tb_email_log (event_key, recipients, status, error_message, context) VALUES (?, ?, ?, ?, ?) "); $stmt->execute([ $eventKey, json_encode($recipients, JSON_UNESCAPED_UNICODE), $status, $error, json_encode($context, JSON_UNESCAPED_UNICODE) ]); } } /modules/master/master.php <!-- REENVIAR CONVITE --> <?php if ($u['estado'] === 'pendente'): ?> <form method="post" action="master_user_resend_invite.php" class="d-inline" onsubmit="return confirm('Reenviar convite por email?')"> <input type="hidden" name="csrf_token" value="<?= csrfToken() ?>"> <input type="hidden" name="id_user" value="<?= $u['id_user'] ?>"> <button class="btn btn-sm btn-outline-info" title="Reenviar convite"> <i class="bi bi-envelope"></i> </button> </form> <?php endif; ?> <?php elseif ($u['estado'] === 'inativo'): ?> /modules/master/master_user_resend_invite.php <?php declare(strict_types=1); require '../../config/config.php'; requireMasterLogin(); requireRole(['OWNER','SUPER_ADMIN']); validateCsrf($_POST['csrf_token'] ?? ''); $userId = (int)($_POST['id_user'] ?? 0); if ($userId <= 0) { $_SESSION['error'] = 'Utilizador inválido.'; header('Location: master.php'); exit; } /* USER */ $stmt = $pdo->prepare(" SELECT id_user, nome, email, estado FROM tb_master_user WHERE id_user = ? "); $stmt->execute([$userId]); $user = $stmt->fetch(PDO::FETCH_ASSOC); if (!$user || $user['estado'] !== 'pendente') { $_SESSION['error'] = 'Utilizador inválido ou não pendente.'; header('Location: master.php'); exit; } /* TOKEN */ $token = bin2hex(random_bytes(32)); $expires = date('Y-m-d H:i:s', strtotime('+48 hours')); $pdo->prepare(" INSERT INTO tb_user_invite_token (id_user, token, expires_at) VALUES (?, ?, ?) ")->execute([$userId, $token, $expires]); $link = BASE_URL . "/auth/define_password.php?token={$token}"; /* EMAIL */ EmailService::send( 'INVITE_MASTER_USER', [ 'user_name' => $user['nome'], 'invite_link' => $link ], [ 'id_user' => $userId, 'origin' => 'resend_invite' ] ); $_SESSION['success'] = 'Convite reenviado com sucesso.'; header('Location: master.php'); exit; Inicialmente fiz a configuração de email, sem passar pelas regras funcionou. Agora que inseri as regras, penso que nao chama nem a funçãoEmailService::send Ja não sei mais como fazer. Isto está no meu servidor. Agradeço muito desde ja a todos que me possam ajudar! Obrigado e um bom ano!!! "Innovation distinguishes between a leader and a follower." Steve jobs.
JakeBass Posted January 1, 2026 at 04:54 AM Report #635498 Posted January 1, 2026 at 04:54 AM E o quê que te aparece nos logs?
security Posted January 1, 2026 at 12:18 PM Author Report #635499 Posted January 1, 2026 at 12:18 PM Em 01/01/2026 às 04:54, JakeBass disse: E o quê que te aparece nos logs? Absolutamente nada! Teoricamente esta correto mas na prática parece que nao chama a função. Esta a falhar silenciosamente !!! "Innovation distinguishes between a leader and a follower." Steve jobs.
security Posted January 1, 2026 at 03:36 PM Author Report #635500 Posted January 1, 2026 at 03:36 PM Entretanto decidi fazer um ficheiro de teste para me apresentar o passo a passo, para ver onde passava a ordem, não é um formato complexo e o resultado surpreendeu, segundo o teste passou por todo lado que tinha de passar e chegou ao final com sucesso!!! == TESTE EmailService == ✔ PDO OK ✔ Evento encontrado (id_event=4) ✔ Template encontrado ✔ Destinatários configurados: Array ( [0] => Array ( [recipient_type] => EMAIL [recipient_value] => mabpinto@outlook.pt ) ) == A chamar EmailService::send() == ✔ EmailService::send() executado SEM exception == FIM DO TESTE == "Innovation distinguishes between a leader and a follower." Steve jobs.
JakeBass Posted January 2, 2026 at 06:58 AM Report #635501 Posted January 2, 2026 at 06:58 AM Então o problema deve estar nas configurações SMTP. Experimenta ativar o modo DEBUG no PHPMailer. $mail->SMTPDebug = 2; $mail->Debugoutput = function ($str, $level) { error_log("[SMTP][$level] $str"); }; 1 Report
security Posted January 3, 2026 at 01:08 AM Author Report #635503 Posted January 3, 2026 at 01:08 AM Ja ativei, nao aparece log nenhum. Isso e o mais estranho.. mas irei tentar novamente. Depois coloco o resultado dos logs "Innovation distinguishes between a leader and a follower." Steve jobs.
Rui Carlos Posted January 3, 2026 at 02:34 PM Report #635504 Posted January 3, 2026 at 02:34 PM Já verificaste nos logs to servidor SMTP se há alguma coisa? Adicionalmente, podes adicionar logs no PHP para ver o que é que está efectivamente a ser executado. Rui Carlos Gonçalves
Solution security Posted January 7, 2026 at 08:03 AM Author Solution Report #635516 Posted January 7, 2026 at 08:03 AM Sim ja, nao estava a aparecer nada dlnos logs. Mudei a forma como enviava os emails, em vez de enviar direto atraves do meu email, experimentei usar a brevo.com mas ainda assim apenas consegui enviar o email de teste, quando inseria no sistema falhava o email. Aí já aparecia nos logs authentication falied. A solucao foi adaptar o codigo para enviar os emails atraves da API da brevo. Agora consigo enviar sem problemas. Para conseguir fazer isso tive de criar uma espécie de email-tester a parte. No meu projecto desvio o envio de emails para esse email tester e assim ja consigo enviar, além que tambem consigo usar com outros aistemas "Innovation distinguishes between a leader and a follower." Steve jobs.
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now