Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 93
0.00% covered (danger)
0.00%
0 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
AI
0.00% covered (danger)
0.00%
0 / 93
0.00% covered (danger)
0.00%
0 / 5
552
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 send_email
0.00% covered (danger)
0.00%
0 / 77
0.00% covered (danger)
0.00%
0 / 1
210
 isEmailValid
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 isValidHtml
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
2
 isValidEmailSubject
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
30
1<?php
2
3namespace App\Http\Controllers;
4
5use App\Exceptions\AppException;
6use App\Services\SendgridLogger;
7use Illuminate\Contracts\Routing\ResponseFactory;
8use Illuminate\Http\Request;
9use Illuminate\Http\Response;
10use Illuminate\Support\Facades\Log;
11use Illuminate\Support\Facades\Validator;
12use SendGrid\Mail\Mail;
13
14class AI extends Controller
15{
16    public function __construct() {}
17
18    public function send_email(Request $request): ResponseFactory|Response
19    {
20
21        try {
22
23            $data = $request->all();
24
25            $body = '';
26            $subject = '';
27
28            $validator = Validator::make($data, [
29                'email' => ['required', 'email'],
30                'subject' => ['required'],
31                'body' => ['required'],
32            ]);
33
34            if ($validator->fails()) {
35                if ($validator->errors()->has('email')
36                    && ! $request->has('email')) {
37
38                    return response()->json([
39                        'status' => 'error',
40                        'message' => 'Missing required field: email',
41                        'code' => 'MISSING_FIELD',
42                    ], 400);
43                }
44
45                if (! $this->isEmailValid($data['email'])) {
46                    return response([
47                        'status' => 'error',
48                        'message' => 'Invalid email address format!',
49                        'code' => 'INVALID_EMAIL',
50                    ], 400);
51                }
52
53                if ($validator->errors()->has('subject')
54                    && ! $request->has('subject')) {
55
56                    return response()->json([
57                        'status' => 'error',
58                        'message' => 'Missing required field: subject',
59                        'code' => 'MISSING_FIELD',
60                    ], 400);
61                }
62
63                if (! $this->isValidEmailSubject($data['subject'])) {
64                    return response([
65                        'status' => 'error',
66                        'message' => 'Invalid email subject!',
67                        'code' => 'INVALID_SUBJECT',
68                    ], 400);
69                }
70
71                if ($validator->errors()->has('body')
72                    && ! $request->has('body')) {
73
74                    return response()->json([
75                        'status' => 'error',
76                        'message' => 'Missing required field: body',
77                        'code' => 'MISSING_FIELD',
78                    ], 400);
79                }
80
81                if (! $this->isValidHtml($data['body'])) {
82                    return response()->json([
83                        'status' => 'error',
84                        'message' => 'Invalid HTML!',
85                        'code' => 'INVALID_BODY',
86                    ], 422);
87                }
88            }
89
90            $toEmail = $data['email'];
91            $body = $data['body'];
92            $subject = $data['subject'];
93
94            $mail = new Mail;
95
96            $mail->setFrom('fire@fire.es', 'Fire Service Titan');
97            $mail->addTo($toEmail);
98            $mail->setSubject($subject);
99            $mail->addContent('text/html', $body);
100
101            $sendgrid = new \SendGrid(config('services.sendgrid.api_key'));
102
103            Log::channel('email_log')->info(print_r($data, true));
104
105            try {
106                $response = $sendgrid->send($mail);
107                SendgridLogger::log($mail, $response);
108            } catch (\Throwable $sendException) {
109                SendgridLogger::logException($mail, $sendException);
110                throw $sendException;
111            }
112
113            if ($response->statusCode() == 202) {
114                return response([
115                    'status' => 'success',
116                    'message' => 'Verification email sent successfully!',
117                    'email' => $toEmail,
118                ], 200);
119            }
120
121            return response([
122                'status' => 'error',
123                'message' => 'Failed to send email. Please try again later.',
124                'code' => 'EMAIL_DELIVERY_FAILED',
125            ], 500);
126
127        } catch (\Exception $e) {
128            report(AppException::fromException($e, 'SEND_VERIFICATION_EMAIL_EXCEPTION'));
129
130            return response(['message' => 'KO', 'error' => $e->getMessage()]);
131        }
132
133    }
134
135    public function isEmailValid($email): bool
136    {
137
138        $pattern = '/^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$/';
139
140        if (preg_match($pattern, (string) $email)) {
141            return true;
142        } else {
143            return false;
144        }
145
146    }
147
148    private function isValidHtml($html)
149    {
150        libxml_use_internal_errors(true);
151
152        $dom = new \DOMDocument;
153        $isValid = $dom->loadHTML($html, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
154
155        libxml_clear_errors();
156
157        return $isValid;
158    }
159
160    public function isValidEmailSubject(string $subject): bool
161    {
162        $subject = trim($subject);
163
164        if ($subject === '' || mb_strlen($subject) < 3 || mb_strlen($subject) > 150) {
165            return false;
166        }
167
168        if (preg_match('/[\r\n]/', $subject)) {
169            return false;
170        }
171
172        return true;
173    }
174}