Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
b899cd9
fix: sync user/company to sandbox before creating test API key
Apr 21, 2026
1208f24
feat: schedule sandbox:sync to run hourly
Apr 21, 2026
4c65e40
fix: restore cross-organisation invite flow for existing users
Apr 21, 2026
561d3cd
fix: correct fleetbase.db.connection config key typo in Model and Set…
Apr 21, 2026
3865ec7
fix: remove Model/Setting constructors that broke sandbox connection …
Apr 21, 2026
e1814fc
fix: correct config key typo and pin Invite to production DB via getC…
Apr 21, 2026
676fabc
fix: remove connection-overriding constructors from Model and Setting
Apr 21, 2026
f39a144
revert: restore Model.php and Setting.php to match main exactly
Apr 21, 2026
20f392c
fix: update fleetbase.connection.db in setSandboxSession when switchi…
Apr 21, 2026
5341ce9
fix: pin Invite::getConnectionName() to env DB_CONNECTION, not runtim…
Apr 21, 2026
6702f59
fix: correct fleetbase.db.connection config key typo in Model and Set…
Apr 21, 2026
7a520ad
fix: resolve 500 on /users/me after invite acceptance
Apr 21, 2026
04fb3fe
fix: persist and apply role_uuid through the full invite flow
Apr 21, 2026
c0e1df6
fix: restore phone uniqueness rule in CreateUserRequest
Apr 21, 2026
1363cd4
refactor: store invite role_uuid in meta JSON via HasMetaAttributes
Apr 21, 2026
0d4320c
fix: include pending-invite users in IAM user list
Apr 21, 2026
7c5d1ca
fix: sync company_users to sandbox so invited users appear in IAM list
Apr 21, 2026
a7294ad
fix: pin User, Company, CompanyUser to production connection via getC…
Apr 21, 2026
9f92a01
refactor: remove redundant invite subquery from UserFilter
Apr 21, 2026
684d01c
feat: include CompanyUser in sandbox:sync for structural consistency
Apr 21, 2026
d5ffc7a
fix: query users from production connection in UserController only
Apr 21, 2026
bb1d8a4
Ran linter and fixed invite flow
roncodes Apr 21, 2026
f38d47c
v1.6.41
roncodes Apr 21, 2026
d3aa933
Merge pull request #203 from fleetbase/fix/sandbox-api-key-fk-constraint
roncodes Apr 21, 2026
81bef54
Merge pull request #204 from fleetbase/feat/schedule-sandbox-sync
roncodes Apr 21, 2026
7198929
Merge pull request #205 from fleetbase/fix/cross-org-invite-flow
roncodes Apr 21, 2026
c428388
ran linter
roncodes Apr 21, 2026
b28bede
Fixed to use the correct connection at runtime as setting config valu…
roncodes Apr 21, 2026
383b9c5
Fix invitation flow
roncodes Apr 21, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.40",
"version": "1.6.41",
"description": "Core Framework and Resources for Fleetbase API",
"keywords": [
"fleetbase",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@
* 'pending' is retained for backwards compatibility (e.g. manually-created items
* that have not yet been confirmed).
*/
return new class extends Migration
{
return new class extends Migration {
public function up(): void
{
DB::statement("
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;

return new class extends Migration {
public function up(): void
Expand All @@ -13,13 +13,13 @@ public function up(): void
});

// Backfill from the parent schedule
DB::statement("
DB::statement('
UPDATE schedule_items si
JOIN schedules s ON s.uuid = si.schedule_uuid
SET si.company_uuid = s.company_uuid
WHERE si.company_uuid IS NULL
AND si.schedule_uuid IS NOT NULL
");
');
}

public function down(): void
Expand Down
36 changes: 36 additions & 0 deletions migrations/2026_04_21_000001_add_role_uuid_to_invites_table.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration {
/**
* Run the migrations.
*
* Adds a nullable JSON `meta` column to the `invites` table so that
* arbitrary key-value data (e.g. role_uuid for user invitations) can be
* stored on an invite record without requiring dedicated columns for each
* use-case. The HasMetaAttributes trait is used to read and write values.
*
* @return void
*/
public function up()
{
Schema::table('invites', function (Blueprint $table) {
$table->json('meta')->nullable()->after('reason');
});
}

/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('invites', function (Blueprint $table) {
$table->dropColumn('meta');
});
}
};
5 changes: 4 additions & 1 deletion src/Console/Commands/SyncSandbox.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,13 @@ public function handle()
DB::connection('sandbox')
->table('api_credentials')
->truncate();
DB::connection('sandbox')
->table('company_users')
->truncate();
}

// Models that need to be synced from Production to Sandbox
$syncable = [\Fleetbase\Models\User::class, \Fleetbase\Models\Company::class, \Fleetbase\Models\ApiCredential::class];
$syncable = [\Fleetbase\Models\User::class, \Fleetbase\Models\Company::class, \Fleetbase\Models\CompanyUser::class, \Fleetbase\Models\ApiCredential::class];

// Sync each syncable data model
foreach ($syncable as $model) {
Expand Down
123 changes: 123 additions & 0 deletions src/Http/Controllers/Internal/v1/ApiCredentialController.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,14 @@
use Fleetbase\Http\Controllers\FleetbaseController;
use Fleetbase\Http\Requests\ExportRequest;
use Fleetbase\Models\ApiCredential;
use Fleetbase\Models\Company;
use Fleetbase\Models\CompanyUser;
use Fleetbase\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Str;
use Maatwebsite\Excel\Facades\Excel;

Expand All @@ -28,6 +33,124 @@ class ApiCredentialController extends FleetbaseController
*/
public $service = 'developers';

/**
* Create a new API credential record.
*
* Overrides the generic createRecord to ensure that when a test/sandbox key
* is being created, the current user and company are mirrored into the sandbox
* database first. Without this, the foreign key constraint on `api_credentials.user_uuid`
* (which references `users.uuid` in the sandbox DB) will fail because the user
* and company rows only exist in the production database.
*
* @return \Illuminate\Http\Response
*/
public function createRecord(Request $request)
{
// Determine if this is a sandbox/test key creation request.
$isSandbox = \Fleetbase\Support\Utils::isTrue($request->header('Access-Console-Sandbox'));

if ($isSandbox) {
// Ensure the current user and company exist in the sandbox DB before
// attempting the insert, so the FK constraints are satisfied.
$this->syncCurrentSessionToSandbox($request);
}

return parent::createRecord($request);
}

/**
* Mirrors the currently authenticated user, their company, and the company–user
* membership record into the sandbox database.
*
* This is a targeted, on-demand version of the `sandbox:sync` Artisan command,
* scoped only to the records needed to satisfy the foreign key constraints when
* inserting a new test-mode `api_credentials` row.
*/
protected function syncCurrentSessionToSandbox(Request $request): void
{
$userUuid = session('user');
$companyUuid = session('company');

if (!$userUuid || !$companyUuid) {
return;
}

// Temporarily disable FK checks so we can upsert in any order.
Schema::connection('sandbox')->disableForeignKeyConstraints();

try {
// --- Sync User ---
$user = User::on('mysql')->withoutGlobalScopes()->where('uuid', $userUuid)->first();
if ($user) {
$this->upsertModelToSandbox($user);
}

// --- Sync Company ---
$company = Company::on('mysql')->withoutGlobalScopes()->where('uuid', $companyUuid)->first();
if ($company) {
$this->upsertModelToSandbox($company);
}

// --- Sync CompanyUser pivot ---
$companyUser = CompanyUser::on('mysql')
->withoutGlobalScopes()
->where('user_uuid', $userUuid)
->where('company_uuid', $companyUuid)
->first();
if ($companyUser) {
$this->upsertModelToSandbox($companyUser);
}
} finally {
Schema::connection('sandbox')->enableForeignKeyConstraints();
}
}

/**
* Upserts a single Eloquent model record into the sandbox database.
*
* Mirrors the approach used in the `sandbox:sync` Artisan command:
* reduces the record to its fillable attributes, normalises datetime
* fields to strings, JSON-encodes any Json-cast columns, then performs
* an `updateOrInsert` keyed on `uuid`.
*/
protected function upsertModelToSandbox(\Illuminate\Database\Eloquent\Model $model): void
{
$clone = collect($model->toArray())
->only($model->getFillable())
->toArray();

if (!isset($clone['uuid']) || !is_string($clone['uuid'])) {
return;
}

// Normalise datetime columns to plain strings.
foreach ($clone as $key => $value) {
if (isset($clone[$key]) && Str::endsWith($key, '_at')) {
try {
$clone[$key] = Carbon::parse($clone[$key])->toDateTimeString();
} catch (\Exception $e) {
$clone[$key] = null;
}
}
}

// JSON-encode any Json-cast columns that are still arrays/objects.
$jsonColumns = collect($model->getCasts())
->filter(fn ($cast) => Str::contains($cast, 'Json'))
->keys()
->toArray();

foreach ($clone as $key => $value) {
if (in_array($key, $jsonColumns) && (is_object($value) || is_array($value))) {
$clone[$key] = json_encode($value);
}
}

DB::connection('sandbox')
->table($model->getTable())
->updateOrInsert(['uuid' => $clone['uuid']], $clone);
}

/**
* Export the companies/users api credentials to excel or csv.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@ class ScheduleExceptionController extends FleetbaseController

/**
* The ScheduleService instance.
*
* @var ScheduleService
*/
protected ScheduleService $scheduleService;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@ class ScheduleTemplateController extends FleetbaseController

/**
* The ScheduleService instance.
*
* @var ScheduleService
*/
protected ScheduleService $scheduleService;

Expand Down
Loading
Loading