feat: Implement SMS sending functionality with KavehNegar and Rangineh providers
- Add SendSmsMessage class for encapsulating SMS message data. - Create KavehNegarProvider and RanginehProvider classes implementing SmsProviderInterface for sending SMS. - Implement SmsLogRepository and SmsTemplateRepository for managing SMS logs and templates. - Develop SendSmsHandler for handling SMS sending messages. - Create SmsService to manage SMS dispatching and logging. - Add UserProfileController for managing user profiles with CRUD operations. - Implement UserProfile entity and repository for user profile data management. - Update symfony.lock and bootstrap.php for project dependencies and environment setup.
This commit is contained in:
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
namespace App\Shared\Service;
|
||||
|
||||
use App\Shared\Constant\ErrorCodes;
|
||||
use App\Shared\Exception\AppException;
|
||||
use Symfony\Component\HttpFoundation\File\UploadedFile;
|
||||
|
||||
class FileValidatorService
|
||||
{
|
||||
private const ALLOWED_SIGNATURES = [
|
||||
'image/jpeg' => ["\xFF\xD8\xFF"],
|
||||
'image/png' => ["\x89\x50\x4E\x47\x0D\x0A\x1A\x0A"],
|
||||
'image/webp' => ["RIFF"],
|
||||
];
|
||||
|
||||
private const ALLOWED_EXTENSIONS = ['jpg', 'jpeg', 'png', 'webp'];
|
||||
|
||||
public function __construct(private readonly int $maxSizeBytes = 5_242_880) {}
|
||||
|
||||
public function validateUploadedFile(UploadedFile $file): string
|
||||
{
|
||||
if ($file->getSize() > $this->maxSizeBytes) {
|
||||
throw new AppException(ErrorCodes::ERR_FILE_002, null, 422);
|
||||
}
|
||||
|
||||
$binaryContent = (string) file_get_contents($file->getPathname());
|
||||
return $this->validate($binaryContent, $file->getClientOriginalName());
|
||||
}
|
||||
|
||||
public function validate(string $binaryContent, string $claimedFilename): string
|
||||
{
|
||||
if (strlen($binaryContent) > $this->maxSizeBytes) {
|
||||
throw new AppException(ErrorCodes::ERR_FILE_002, null, 422);
|
||||
}
|
||||
|
||||
$detected = false;
|
||||
foreach (self::ALLOWED_SIGNATURES as $signatures) {
|
||||
foreach ($signatures as $sig) {
|
||||
if (str_starts_with($binaryContent, $sig)) {
|
||||
$detected = true;
|
||||
break 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!$detected) {
|
||||
throw new AppException(ErrorCodes::ERR_FILE_001, null, 422);
|
||||
}
|
||||
|
||||
return $this->sanitizeFilename($claimedFilename);
|
||||
}
|
||||
|
||||
public function sanitizeFilename(string $filename): string
|
||||
{
|
||||
$safeName = preg_replace('/[^a-zA-Z0-9._-]/', '', basename($filename));
|
||||
if (empty($safeName) || str_contains($safeName, '..')) {
|
||||
throw new AppException(ErrorCodes::ERR_FILE_001, null, 422);
|
||||
}
|
||||
|
||||
$ext = strtolower(pathinfo($safeName, PATHINFO_EXTENSION));
|
||||
if (!in_array($ext, self::ALLOWED_EXTENSIONS, true)) {
|
||||
throw new AppException(ErrorCodes::ERR_FILE_001, null, 422);
|
||||
}
|
||||
|
||||
return $safeName;
|
||||
}
|
||||
|
||||
public function detectMimeType(string $filePath): string
|
||||
{
|
||||
$handle = fopen($filePath, 'rb');
|
||||
$header = fread($handle, 12);
|
||||
fclose($handle);
|
||||
|
||||
foreach (self::ALLOWED_SIGNATURES as $mime => $signatures) {
|
||||
foreach ($signatures as $sig) {
|
||||
if (str_starts_with($header, $sig)) {
|
||||
return $mime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new AppException(ErrorCodes::ERR_FILE_001, 'نوع فایل پشتیبانی نمیشود', 422);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user