Skip to content

Commit 423e464

Browse files
committed
feat: Improved file download endpoint, removed query optimizer, fixed session caching
1 parent 9020d12 commit 423e464

12 files changed

Lines changed: 132 additions & 76 deletions

File tree

‎composer.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "fleetbase/core-api",
3-
"version": "1.6.30",
3+
"version": "1.6.31",
44
"description": "Core Framework and Resources for Fleetbase API",
55
"keywords": [
66
"fleetbase",

‎src/Http/Controllers/Internal/v1/AuthController.php‎

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -115,10 +115,11 @@ public function session(Request $request)
115115
}
116116

117117
$sessionData = [
118-
'token' => $request->bearerToken(),
119-
'user' => $user->uuid,
120-
'verified' => $user->isVerified(),
121-
'type' => $user->getType(),
118+
'token' => $request->bearerToken(),
119+
'user' => $user->uuid,
120+
'verified' => $user->isVerified(),
121+
'type' => $user->getType(),
122+
'last_modified' => $user->updated_at,
122123
];
123124

124125
if (session()->has('impersonator')) {
@@ -132,7 +133,15 @@ public function session(Request $request)
132133
return response()->error('Session has expired.', 401, ['restore' => false]);
133134
}
134135

135-
return response()->json($session)->header('Cache-Control', 'private, max-age=300'); // 5 minutes
136+
// Generate an etag
137+
$etag = sha1(json_encode($session));
138+
139+
return response()
140+
->json($session)
141+
->setEtag($etag)
142+
->setLastModified($session['last_modified'])
143+
->header('Cache-Control', 'private, no-cache, must-revalidate')
144+
->header('X-Cache-Hit', 'false');
136145
}
137146

138147
/**
@@ -661,6 +670,7 @@ public function getUserOrganizations(Request $request)
661670
*/
662671
$etagPayload = $companies->map(function ($company) {
663672
$ownerTimestamp = $company->owner?->updated_at?->timestamp ?? 0;
673+
664674
return $company->uuid . ':' . $company->updated_at->timestamp . ':' . $ownerTimestamp;
665675
})->join('|');
666676

‎src/Http/Controllers/Internal/v1/FileController.php‎

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -312,15 +312,32 @@ public function uploadBase64(UploadBase64FileRequest $request)
312312
/**
313313
* Handle file download.
314314
*
315-
* @return \Illuminate\Http\Response
315+
* Supports both:
316+
* - /download/{id}
317+
* - /download?file={id}
318+
*
319+
* @param string|null $id File UUID from route parameter
320+
*
321+
* @return \Symfony\Component\HttpFoundation\StreamedResponse
316322
*/
317-
public function download(?string $id, DownloadFileRequest $request)
323+
public function download(DownloadFileRequest $request, ?string $id = null)
318324
{
319-
$disk = $request->input('disk', config('filesystems.default'));
320-
$file = File::where('uuid', $id)->first();
325+
// Resolve file ID from route or query string
326+
$fileId = $id ?? $request->query('file');
327+
if (!$fileId) {
328+
abort(400, 'Missing file identifier.');
329+
}
330+
331+
$disk = $request->input('disk', config('filesystems.default'));
332+
$file = File::where('uuid', $fileId)->firstOrFail();
333+
$filename = $file->original_filename ?: basename($file->path);
334+
321335
/** @var \Illuminate\Filesystem\FilesystemAdapter $filesystem */
322336
$filesystem = Storage::disk($disk);
323337

324-
return $filesystem->download($file->path, $file->original_filename);
338+
return $filesystem->download(
339+
$file->path,
340+
$filename
341+
);
325342
}
326343
}

‎src/Http/Controllers/Internal/v1/UserController.php‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,11 @@
2323
use Fleetbase\Models\User;
2424
use Fleetbase\Notifications\UserAcceptedCompanyInvite;
2525
use Fleetbase\Notifications\UserInvited;
26+
use Fleetbase\Services\UserCacheService;
2627
use Fleetbase\Support\Auth;
2728
use Fleetbase\Support\NotificationRegistry;
2829
use Fleetbase\Support\TwoFactorAuth;
2930
use Fleetbase\Support\Utils;
30-
use Fleetbase\Services\UserCacheService;
3131
use Illuminate\Http\Request;
3232
use Illuminate\Support\Arr;
3333
use Illuminate\Support\Carbon;
@@ -181,7 +181,7 @@ public function current(Request $request)
181181
$etag = UserCacheService::generateETag($user);
182182

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

187187
if ($cachedData) {
@@ -198,7 +198,7 @@ public function current(Request $request)
198198
$user->loadCompanyUser();
199199

200200
// Transform to resource
201-
$userData = new $this->resource($user);
201+
$userData = new $this->resource($user);
202202
$userArray = $userData->toArray($request);
203203

204204
// Store in cache

‎src/Http/Middleware/ValidateETag.php‎

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
namespace Fleetbase\Http\Middleware;
44

5-
use Closure;
65
use Illuminate\Http\Request;
76

87
class ValidateETag
@@ -13,13 +12,8 @@ class ValidateETag
1312
* This middleware checks if the client sent an If-None-Match header with an ETag.
1413
* If the ETag matches the response ETag, it returns a 304 Not Modified response,
1514
* allowing the browser to use its cached version.
16-
*
17-
* @param \Illuminate\Http\Request $request
18-
* @param \Closure $next
19-
*
20-
* @return mixed
2115
*/
22-
public function handle(Request $request, Closure $next)
16+
public function handle(Request $request, \Closure $next)
2317
{
2418
// Handle the request and get response
2519
$response = $next($request);

‎src/Http/Requests/Internal/DownloadFileRequest.php‎

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,20 @@ public function authorize()
1616
return $this->session()->has('user');
1717
}
1818

19+
/**
20+
* Prepare the data for validation.
21+
*
22+
* Ensures route parameters are available for validation rules.
23+
*/
24+
protected function prepareForValidation(): void
25+
{
26+
if ($this->route('id')) {
27+
$this->merge([
28+
'id' => $this->route('id'),
29+
]);
30+
}
31+
}
32+
1933
/**
2034
* Get the validation rules that apply to the request.
2135
*
@@ -24,7 +38,9 @@ public function authorize()
2438
public function rules()
2539
{
2640
return [
27-
'id' => ['required', 'string', 'exists:files,uuid'],
41+
'file' => ['required_without:id', 'uuid', 'exists:files,uuid'],
42+
'id' => ['required_without:file', 'uuid', 'exists:files,uuid'],
43+
'disk' => ['sometimes', 'string'],
2844
];
2945
}
3046

@@ -36,8 +52,20 @@ public function rules()
3652
public function messages()
3753
{
3854
return [
39-
'id.required' => 'Please provide a file ID.',
55+
// Missing identifier
56+
'id.required_without' => 'Please provide a file identifier.',
57+
'file.required_without' => 'Please provide a file identifier.',
58+
59+
// Invalid format
60+
'id.uuid' => 'The file identifier must be a valid UUID.',
61+
'file.uuid' => 'The file identifier must be a valid UUID.',
62+
63+
// File not found
4064
'id.exists' => 'The requested file does not exist.',
65+
'file.exists' => 'The requested file does not exist.',
66+
67+
// Disk override
68+
'disk.string' => 'The storage disk must be a valid string.',
4169
];
4270
}
4371
}

‎src/Observers/UserObserver.php‎

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,6 @@ class UserObserver
1111
{
1212
/**
1313
* Handle the User "updated" event.
14-
*
15-
* @param \Fleetbase\Models\User $user
16-
*
17-
* @return void
1814
*/
1915
public function updated(User $user): void
2016
{
@@ -46,10 +42,6 @@ public function deleted(User $user)
4642

4743
/**
4844
* Handle the User "restored" event.
49-
*
50-
* @param \Fleetbase\Models\User $user
51-
*
52-
* @return void
5345
*/
5446
public function restored(User $user): void
5547
{
@@ -66,10 +58,6 @@ public function restored(User $user): void
6658
* This clears the cached organizations list which includes owner relationships.
6759
* When a user updates their profile and they are an owner of organizations,
6860
* the cached organization data needs to be refreshed to reflect the updated owner info.
69-
*
70-
* @param \Fleetbase\Models\User $user
71-
*
72-
* @return void
7361
*/
7462
private function invalidateOrganizationsCache(User $user): void
7563
{

‎src/Services/UserCacheService.php‎

Lines changed: 1 addition & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,6 @@ class UserCacheService
2727
* Generate cache key for a user and company.
2828
*
2929
* @param int|string $userId
30-
* @param string $companyId
31-
*
32-
* @return string
3330
*/
3431
public static function getCacheKey($userId, string $companyId): string
3532
{
@@ -40,9 +37,6 @@ public static function getCacheKey($userId, string $companyId): string
4037
* Get cached user data.
4138
*
4239
* @param int|string $userId
43-
* @param string $companyId
44-
*
45-
* @return array|null
4640
*/
4741
public static function get($userId, string $companyId): ?array
4842
{
@@ -75,11 +69,6 @@ public static function get($userId, string $companyId): ?array
7569
* Store user data in cache.
7670
*
7771
* @param int|string $userId
78-
* @param string $companyId
79-
* @param array $data
80-
* @param int|null $ttl
81-
*
82-
* @return bool
8372
*/
8473
public static function put($userId, string $companyId, array $data, ?int $ttl = null): bool
8574
{
@@ -110,10 +99,6 @@ public static function put($userId, string $companyId, array $data, ?int $ttl =
11099

111100
/**
112101
* Invalidate cache for a specific user.
113-
*
114-
* @param User $user
115-
*
116-
* @return void
117102
*/
118103
public static function invalidateUser(User $user): void
119104
{
@@ -157,9 +142,6 @@ public static function invalidateUser(User $user): void
157142
* Invalidate cache for a specific user and company.
158143
*
159144
* @param int|string $userId
160-
* @param string $companyId
161-
*
162-
* @return void
163145
*/
164146
public static function invalidate($userId, string $companyId): void
165147
{
@@ -184,16 +166,12 @@ public static function invalidate($userId, string $companyId): void
184166

185167
/**
186168
* Invalidate all cache for a company.
187-
*
188-
* @param string $companyId
189-
*
190-
* @return void
191169
*/
192170
public static function invalidateCompany(string $companyId): void
193171
{
194172
try {
195173
// Get all cache keys for this company
196-
$pattern = self::CACHE_PREFIX . '*:' . $companyId;
174+
$pattern = self::CACHE_PREFIX . '*:' . $companyId;
197175
$cacheKeys = Cache::getRedis()->keys($pattern);
198176

199177
foreach ($cacheKeys as $key) {
@@ -216,10 +194,6 @@ public static function invalidateCompany(string $companyId): void
216194

217195
/**
218196
* Generate ETag for a user.
219-
*
220-
* @param User $user
221-
*
222-
* @return string
223197
*/
224198
public static function generateETag(User $user): string
225199
{
@@ -228,8 +202,6 @@ public static function generateETag(User $user): string
228202

229203
/**
230204
* Get browser cache TTL.
231-
*
232-
* @return int
233205
*/
234206
public static function getBrowserCacheTTL(): int
235207
{
@@ -238,8 +210,6 @@ public static function getBrowserCacheTTL(): int
238210

239211
/**
240212
* Get server cache TTL.
241-
*
242-
* @return int
243213
*/
244214
public static function getServerCacheTTL(): int
245215
{
@@ -248,8 +218,6 @@ public static function getServerCacheTTL(): int
248218

249219
/**
250220
* Check if caching is enabled.
251-
*
252-
* @return bool
253221
*/
254222
public static function isEnabled(): bool
255223
{
@@ -258,8 +226,6 @@ public static function isEnabled(): bool
258226

259227
/**
260228
* Clear all user current caches.
261-
*
262-
* @return void
263229
*/
264230
public static function flush(): void
265231
{

0 commit comments

Comments
 (0)