Skip to content
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "fleetbase/core-api",
"version": "1.6.35",
"version": "1.6.36",
"description": "Core Framework and Resources for Fleetbase API",
"keywords": [
"fleetbase",
Expand Down
4 changes: 2 additions & 2 deletions src/Http/Controllers/Internal/v1/UserController.php
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ public function current(Request $request)

// Try to get from server cache
$companyId = session('company');
$cachedData = UserCacheService::get($user->id, $companyId);
$cachedData = UserCacheService::get($user, $companyId);

if ($cachedData) {
// Return cached data with cache headers
Expand All @@ -202,7 +202,7 @@ public function current(Request $request)
$userArray = $userData->toArray($request);

// Store in cache
UserCacheService::put($user->id, $companyId, $userArray);
UserCacheService::put($user, $companyId, $userArray);

// Return with cache headers
return response()->json(['user' => $userArray])
Expand Down
31 changes: 10 additions & 21 deletions src/Mail/VerificationMail.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@
use Fleetbase\Models\VerificationCode;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;

class VerificationMail extends Mailable
Expand All @@ -17,12 +15,12 @@ class VerificationMail extends Mailable
/**
* The verification code to email.
*/
private VerificationCode $verificationCode;
public VerificationCode $verificationCode;

/**
* Custom content to render if supplied.
*/
private ?string $content;
public ?string $content;

/**
* Create a new message instance.
Expand All @@ -36,30 +34,21 @@ public function __construct(VerificationCode $verificationCode, ?string $content
}

/**
* Get the message content definition.
*/
public function envelope(): Envelope
{
return new Envelope(
subject: $this->verificationCode->code . ' is your ' . config('app.name') . ' verification code',
);
}

/**
* Get the message content definition.
* Build the message.
*
* @return $this
*/
public function content(): Content
public function build()
{
return new Content(
markdown: 'fleetbase::mail.verification',
with: [
return $this
->subject($this->verificationCode->code . ' is your ' . config('app.name') . ' verification code')
->markdown('fleetbase::mail.verification', [
'appName' => config('app.name'),
'currentHour' => now()->hour,
'user' => $this->verificationCode->subject,
'code' => $this->verificationCode->code,
'type' => $this->verificationCode->for,
'content' => $this->content,
]
);
]);
}
}
2 changes: 2 additions & 0 deletions src/Models/User.php
Original file line number Diff line number Diff line change
Expand Up @@ -1343,6 +1343,8 @@ public function assignSingleRole($role): self
$this->companyUser->assignSingleRole($role);

// Invalidate user cache after role change
// Note: With updated_at in cache key, this provides immediate invalidation
// while the timestamp-based key provides automatic cache busting
\Fleetbase\Services\UserCacheService::invalidateUser($this);

return $this;
Expand Down
5 changes: 3 additions & 2 deletions src/Models/VerificationCode.php
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,9 @@ public function subject()
*/
public static function generateFor($subject = null, $for = 'general_verification', $save = true)
{
$verifyCode = new static();
$verifyCode->for = $for;
$verifyCode = new static();
$verifyCode->for = $for;
$verifyCode->status = 'pending'; // Set default status

if ($subject) {
$verifyCode->setSubject($subject, false);
Expand Down
2 changes: 2 additions & 0 deletions src/Observers/UserObserver.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ class UserObserver
public function updated(User $user): void
{
// Invalidate user cache when user is updated
// Note: With updated_at in cache key, this provides immediate invalidation
// while the timestamp-based key provides automatic cache busting
UserCacheService::invalidateUser($user);

// Invalidate organizations cache (user might be an owner)
Expand Down
12 changes: 7 additions & 5 deletions src/Services/SmsService.php
Original file line number Diff line number Diff line change
Expand Up @@ -161,11 +161,13 @@ protected function sendViaTwilio(string $to, string $text, array $options = []):
'response' => $response,
];
} catch (\Throwable $e) {
return [
'success' => false,
'error' => $e->getMessage(),
'code' => $e->getCode(),
];
// return [
// 'success' => false,
// 'error' => $e->getMessage(),
// 'code' => $e->getCode(),
// ];

throw $e;
}
}

Expand Down
51 changes: 31 additions & 20 deletions src/Services/UserCacheService.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,29 +25,34 @@ class UserCacheService

/**
* Generate cache key for a user and company.
* Includes the user's updated_at timestamp for automatic cache busting.
*
* @param int|string $userId
* @param User $user
* @param string $companyId
* @return string
*/
public static function getCacheKey($userId, string $companyId): string
public static function getCacheKey(User $user, string $companyId): string
{
return self::CACHE_PREFIX . $userId . ':' . $companyId;
return self::CACHE_PREFIX . $user->uuid . ':' . $companyId . ':' . $user->updated_at->timestamp;
}

/**
* Get cached user data.
*
* @param int|string $userId
* @param User $user
* @param string $companyId
* @return array|null
*/
public static function get($userId, string $companyId): ?array
public static function get(User $user, string $companyId): ?array
{
$cacheKey = self::getCacheKey($userId, $companyId);
$cacheKey = self::getCacheKey($user, $companyId);

try {
$cached = Cache::get($cacheKey);

if ($cached) {
Log::debug('User cache hit', [
'user_id' => $userId,
'user_id' => $user->uuid,
'company_id' => $companyId,
'cache_key' => $cacheKey,
]);
Expand All @@ -57,7 +62,7 @@ public static function get($userId, string $companyId): ?array
} catch (\Exception $e) {
Log::error('Failed to get user cache', [
'error' => $e->getMessage(),
'user_id' => $userId,
'user_id' => $user->uuid,
'company_id' => $companyId,
]);

Expand All @@ -68,18 +73,22 @@ public static function get($userId, string $companyId): ?array
/**
* Store user data in cache.
*
* @param int|string $userId
* @param User $user
* @param string $companyId
* @param array $data
* @param int|null $ttl
* @return bool
*/
public static function put($userId, string $companyId, array $data, ?int $ttl = null): bool
public static function put(User $user, string $companyId, array $data, ?int $ttl = null): bool
{
$cacheKey = self::getCacheKey($userId, $companyId);
$cacheKey = self::getCacheKey($user, $companyId);
$ttl = $ttl ?? self::CACHE_TTL;

try {
Cache::put($cacheKey, $data, $ttl);

Log::debug('User cache stored', [
'user_id' => $userId,
'user_id' => $user->uuid,
'company_id' => $companyId,
'cache_key' => $cacheKey,
'ttl' => $ttl,
Expand All @@ -89,7 +98,7 @@ public static function put($userId, string $companyId, array $data, ?int $ttl =
} catch (\Exception $e) {
Log::error('Failed to store user cache', [
'error' => $e->getMessage(),
'user_id' => $userId,
'user_id' => $user->uuid,
'company_id' => $companyId,
]);

Expand All @@ -108,7 +117,7 @@ public static function invalidateUser(User $user): void

// Clear cache for each company
foreach ($companies as $companyId) {
$cacheKey = self::getCacheKey($user->id, $companyId);
$cacheKey = self::getCacheKey($user, $companyId);
Cache::forget($cacheKey);

Log::debug('User cache invalidated', [
Expand All @@ -121,7 +130,7 @@ public static function invalidateUser(User $user): void
// Also clear for current session company if different
$sessionCompany = session('company');
if ($sessionCompany && !in_array($sessionCompany, $companies)) {
$cacheKey = self::getCacheKey($user->id, $sessionCompany);
$cacheKey = self::getCacheKey($user, $sessionCompany);
Cache::forget($cacheKey);

Log::debug('User cache invalidated for session company', [
Expand All @@ -141,24 +150,26 @@ public static function invalidateUser(User $user): void
/**
* Invalidate cache for a specific user and company.
*
* @param int|string $userId
* @param User $user
* @param string $companyId
* @return void
*/
public static function invalidate($userId, string $companyId): void
public static function invalidate(User $user, string $companyId): void
{
$cacheKey = self::getCacheKey($userId, $companyId);
$cacheKey = self::getCacheKey($user, $companyId);

try {
Cache::forget($cacheKey);

Log::debug('User cache invalidated', [
'user_id' => $userId,
'user_id' => $user->uuid,
'company_id' => $companyId,
'cache_key' => $cacheKey,
]);
} catch (\Exception $e) {
Log::error('Failed to invalidate user cache', [
'error' => $e->getMessage(),
'user_id' => $userId,
'user_id' => $user->uuid,
'company_id' => $companyId,
]);
}
Expand Down
9 changes: 9 additions & 0 deletions src/Types/Currency.php
Original file line number Diff line number Diff line change
Expand Up @@ -596,6 +596,15 @@ class Currency implements \JsonSerializable
'decimalSeparator' => '.',
'symbolPlacement' => 'before',
],
'KZT' => [
'code' => 'KZT',
'title' => 'Kazakhstani Tenge',
'symbol' => '₸',
'precision' => 2,
'thousandSeparator' => ' ',
'decimalSeparator' => '.',
'symbolPlacement' => 'after',
],
'KES' => [
'code' => 'KES',
'title' => 'Kenyan Shilling',
Expand Down
6 changes: 3 additions & 3 deletions views/mail/verification.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@
@endif

@if($type === 'email_verification')
@component('mail::button', ['url' => \Fleetbase\Support\Utils::consoleUrl('onboard', ['step' => 'verify-email', 'session' => base64_encode($user->uuid), 'code' => $code ])])
Verify Email
@endcomponent
@component('mail::button', ['url' => \Fleetbase\Support\Utils::consoleUrl('onboard', ['step' => 'verify-email', 'session' => base64_encode($user->uuid), 'code' => $code ])])
Verify Email
@endcomponent
@endif

</x-mail-layout>