Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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.50",
"version": "1.6.51",
"description": "Core Framework and Resources for Fleetbase API",
"keywords": [
"fleetbase",
Expand Down
378 changes: 378 additions & 0 deletions src/Http/Controllers/Internal/v1/AdminMetricsController.php

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions src/Http/Controllers/Internal/v1/ChatChannelController.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ public function createRecord(Request $request)
]);
}

$chatChannel->refresh();
$chatChannel->load(['participants.user', 'lastMessage']);
$this->resource::wrap($this->resourceSingularlName);

Expand Down
248 changes: 248 additions & 0 deletions src/Http/Controllers/Internal/v1/CompanyController.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,18 @@

use Fleetbase\Exports\CompanyExport;
use Fleetbase\Http\Controllers\FleetbaseController;
use Fleetbase\Http\Requests\AdminRequest;
use Fleetbase\Http\Requests\ExportRequest;
use Fleetbase\Http\Resources\Organization;
use Fleetbase\Http\Resources\User as UserResource;
use Fleetbase\Models\Company;
use Fleetbase\Models\CompanyUser;
use Fleetbase\Models\ExtensionInstall;
use Fleetbase\Models\Invite;
use Fleetbase\Models\User;
use Fleetbase\Support\Auth;
use Fleetbase\Support\TwoFactorAuth;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Str;
use Maatwebsite\Excel\Facades\Excel;
Expand Down Expand Up @@ -164,6 +167,178 @@ function ($query) use ($id) {
return UserResource::collection($users);
}

public function extensions(string $id, AdminRequest $request): JsonResponse
{
$company = $this->resolveAdminCompany($id);

if (!$company) {
return response()->json(['error' => 'Organization not found.'], 404);
}

$extensions = ExtensionInstall::where('company_uuid', $company->uuid)
->with('extension')
->latest('created_at')
->get()
->filter(fn ($install) => $install->extension !== null)
->map(function ($install) {
$extension = $install->extension;

return [
'id' => $install->uuid,
'uuid' => $install->uuid,
'extension_id' => $extension->extension_id,
'name' => $extension->display_name ?: $extension->name,
'description' => $extension->description,
'icon' => $extension->fa_icon ?: 'puzzle-piece',
'slug' => $extension->slug,
'key' => $extension->key,
'version' => $extension->version,
'status' => $extension->status ?: 'installed',
'installed_at' => $install->created_at,
];
})
->values();

return response()->json(['extensions' => $extensions]);
}

public function setAdminStatus(string $id, AdminRequest $request): JsonResponse
{
$company = $this->resolveAdminCompany($id);
$status = $request->input('status');

if (!$company) {
return response()->json(['error' => 'Organization not found.'], 404);
}

if (!in_array($status, ['active', 'inactive', 'suspended'], true)) {
return response()->json(['error' => 'Invalid organization status.'], 422);
}

$oldStatus = $company->status;
$company->status = $status === 'active' ? null : $status;
$company->save();

$this->logAdminCompanyActivity($request, $company, 'Organization status changed', [
'old' => ['status' => $oldStatus],
'attributes' => ['status' => $company->status ?: 'active'],
], 'updated');

return response()->json(['company' => new Organization($company->refresh())]);
}

public function setAdminOnboarding(string $id, AdminRequest $request): JsonResponse
{
$company = $this->resolveAdminCompany($id);

if (!$company) {
return response()->json(['error' => 'Organization not found.'], 404);
}

$completed = $request->boolean('completed');
$oldValue = $company->onboarding_completed_at;

$company->onboarding_completed_at = $completed ? now() : null;
$company->onboarding_completed_by_uuid = $completed ? $request->user()->uuid : null;
$company->save();

$this->logAdminCompanyActivity($request, $company, $completed ? 'Organization onboarding marked complete' : 'Organization onboarding marked incomplete', [
'old' => ['onboarding_completed_at' => $oldValue],
'attributes' => ['onboarding_completed_at' => $company->onboarding_completed_at],
], 'updated');

return response()->json(['company' => new Organization($company->refresh())]);
}

public function transferOwnershipAdmin(string $id, AdminRequest $request): JsonResponse
{
$company = $this->resolveAdminCompany($id);
$newOwnerId = $request->input('newOwner');

if (!$company) {
return response()->json(['error' => 'Organization not found.'], 404);
}

$newOwner = $company->getCompanyUser($newOwnerId);

if (!$newOwner) {
return response()->json(['error' => 'The new owner is not a member of this organization.'], 422);
}

$oldOwnerUuid = $company->owner_uuid;
$company->assignOwner($newOwner);

$this->logAdminCompanyActivity($request, $company, 'Organization ownership transferred', [
'old' => ['owner_uuid' => $oldOwnerUuid],
'attributes' => ['owner_uuid' => $newOwner->uuid],
], 'updated');

return response()->json([
'status' => 'ok',
'newOwner' => new UserResource($newOwner),
'company' => new Organization($company->refresh()),
]);
}

public function activateAdminUser(string $id, string $userId, AdminRequest $request): JsonResponse
{
return $this->setAdminCompanyUserStatus($id, $userId, $request, 'active');
}

public function deactivateAdminUser(string $id, string $userId, AdminRequest $request): JsonResponse
{
return $this->setAdminCompanyUserStatus($id, $userId, $request, 'inactive');
}

public function verifyAdminUser(string $id, string $userId, AdminRequest $request): JsonResponse
{
[$company, $user, $companyUser, $error] = $this->resolveAdminCompanyUser($id, $userId);

if ($error) {
return $error;
}

$user->manualVerify();

$this->logAdminCompanyActivity($request, $company, 'Organization user verified', [
'attributes' => ['user_uuid' => $user->uuid, 'email_verified_at' => $user->email_verified_at],
], 'updated', $user);

return response()->json([
'message' => 'User verified',
'user' => new UserResource($user->refresh()),
]);
}

public function removeAdminUser(string $id, string $userId, AdminRequest $request): JsonResponse
{
[$company, $user, $companyUser, $error] = $this->resolveAdminCompanyUser($id, $userId);

if ($error) {
return $error;
}

if ($company->owner_uuid === $user->uuid) {
return response()->json(['error' => 'Transfer ownership before removing the organization owner.'], 422);
}

$companyUser->delete();

$nextCompany = $user->companies()->where('companies.uuid', '!=', $company->uuid)->first();

if ($nextCompany && $user->company_uuid === $company->uuid) {
$user->update(['company_uuid' => $nextCompany->uuid]);
}

event(new UserRemovedFromCompany($user, $company));

$this->logAdminCompanyActivity($request, $company, 'Organization user removed', [
'attributes' => ['user_uuid' => $user->uuid, 'email' => $user->email],
], 'deleted', $user);

return response()->json(['message' => 'User removed']);
}

/**
* Export the users to excel or csv.
*
Expand All @@ -178,6 +353,79 @@ public function export(ExportRequest $request)
return Excel::download(new CompanyExport($selections), $fileName);
}

private function setAdminCompanyUserStatus(string $id, string $userId, AdminRequest $request, string $status): JsonResponse
{
[$company, $user, $companyUser, $error] = $this->resolveAdminCompanyUser($id, $userId);

if ($error) {
return $error;
}

if ($status === 'inactive' && $company->owner_uuid === $user->uuid) {
return response()->json(['error' => 'Transfer ownership before deactivating the organization owner.'], 422);
}

$oldStatus = $companyUser->status;
$companyUser->status = $status;
$companyUser->save();

if ($status === 'active') {
$user->activate();
}

$this->logAdminCompanyActivity($request, $company, $status === 'active' ? 'Organization user activated' : 'Organization user deactivated', [
'old' => ['status' => $oldStatus],
'attributes' => ['status' => $status, 'user_uuid' => $user->uuid],
], 'updated', $user);

return response()->json([
'message' => $status === 'active' ? 'User activated' : 'User deactivated',
'status' => $status,
'user' => new UserResource($user->refresh()),
]);
}

private function resolveAdminCompany(string $id): ?Company
{
return Company::where('uuid', $id)->orWhere('public_id', $id)->first();
}

private function resolveAdminCompanyUser(string $companyId, string $userId): array
{
$company = $this->resolveAdminCompany($companyId);

if (!$company) {
return [null, null, null, response()->json(['error' => 'Organization not found.'], 404)];
}

$user = User::where('uuid', $userId)->orWhere('public_id', $userId)->first();

if (!$user) {
return [$company, null, null, response()->json(['error' => 'User not found.'], 404)];
}

$companyUser = CompanyUser::where(['company_uuid' => $company->uuid, 'user_uuid' => $user->uuid])->first();

if (!$companyUser) {
return [$company, $user, null, response()->json(['error' => 'User is not a member of this organization.'], 404)];
}

return [$company, $user, $companyUser, null];
}

private function logAdminCompanyActivity(AdminRequest $request, Company $company, string $description, array $properties = [], string $event = 'updated', ?User $subject = null): void
{
$activity = activity('admin')
->causedBy($request->user())
->performedOn($subject ?? $company)
->withProperties($properties)
->event($event)
->log($description);

$activity->company_id = $company->uuid;
$activity->save();
}

/**
* Transfer ownership of company to another member, and make them the Administrator.
*
Expand Down
Loading
Loading