refactor(accounts): rewrite legacy-era regions of the provider, admin companies, profile controller and routes in place

This commit is contained in:
Darko Gjorgjijoski
2026-08-20 23:48:22 +02:00
parent 3f5238bc43
commit bc213966ff
6 changed files with 53 additions and 36 deletions
@@ -46,11 +46,19 @@ class AccountsServiceProvider extends ServiceProvider
Gate::policy(User::class, UserPolicy::class);
Gate::policy(Role::class, RolePolicy::class);
Gate::define('create company', [CompanyPolicy::class, 'create']);
Gate::define('transfer company ownership', [CompanyPolicy::class, 'transferOwnership']);
Gate::define('delete company', [CompanyPolicy::class, 'delete']);
Gate::define('manage company', [SettingsPolicy::class, 'manageCompany']);
Gate::define('delete multiple users', [UserPolicy::class, 'deleteMultiple']);
Gate::define('owner only', [OwnerPolicy::class, 'managedByOwner']);
// Ability names the controllers authorize against, each one routed to
// the policy method that decides it. Registered in this order.
$gates = [
'create company' => [CompanyPolicy::class, 'create'],
'transfer company ownership' => [CompanyPolicy::class, 'transferOwnership'],
'delete company' => [CompanyPolicy::class, 'delete'],
'manage company' => [SettingsPolicy::class, 'manageCompany'],
'delete multiple users' => [UserPolicy::class, 'deleteMultiple'],
'owner only' => [OwnerPolicy::class, 'managedByOwner'],
];
foreach ($gates as $ability => $handler) {
Gate::define($ability, $handler);
}
}
}
@@ -68,8 +68,7 @@ class CompaniesController extends Controller
$this->authorize('create company');
$user = $request->user();
$company = Company::create($request->getCompanyPayload());
$company = Company::query()->create($request->getCompanyPayload());
$company->unique_hash = Hashids::connection(HashidConnection::Company->value)->encode($company->id);
$company->save();
$this->companyService->setupDefaults($company, (int) $request->validated('currency'));
@@ -87,12 +86,15 @@ class CompaniesController extends Controller
public function destroy(Request $request)
{
$company = Company::find($request->header('company'));
$company = Company::query()->find($request->header('company'));
$this->authorize('delete company', $company);
if ($request->name !== $company->name) {
return respondJson('company_name_must_match_with_given_name', 'Company name must match with given name');
if ($company->name !== $request->input('name')) {
return respondJson(
'company_name_must_match_with_given_name',
'Company name must match with given name'
);
}
$this->companyService->delete($company);
@@ -104,8 +106,6 @@ class CompaniesController extends Controller
public function userCompanies(Request $request)
{
$companies = $request->user()->companies;
return CompanyResource::collection($companies);
return CompanyResource::collection($request->user()->companies);
}
}
@@ -25,18 +25,27 @@ class UserProfileController extends Controller
public function update(ProfileRequest $request)
{
$user = $request->user();
$account = $request->user();
$user->update($request->validated());
$account->update($request->validated());
return new UserResource($user);
return new UserResource($account);
}
/**
* Attach, swap or drop the caller's profile picture.
*
* Three things can arrive on one call and each is handled in turn, so a
* payload carrying both a removal flag and a picture ends up with the
* picture, and a payload carrying both transports ends up with whatever
* came in as base64. Only the two picture branches check that somebody is
* actually signed in; the removal branch does not. Kept as it stands.
*/
public function uploadAvatar(AvatarRequest $request)
{
$user = auth()->user();
$user = $request->user();
if (isset($request->is_admin_avatar_removed) && (bool) $request->is_admin_avatar_removed) {
if ($request->is_admin_avatar_removed ?? false) {
$this->userAvatarManager->clear($user);
}
if ($user && $request->hasFile('admin_avatar')) {
@@ -48,8 +57,9 @@ class UserProfileController extends Controller
);
}
if ($user && $request->has('avatar')) {
$data = json_decode($request->avatar);
if ($user !== null && $request->has('avatar')) {
$encoded = $request->avatar;
$data = json_decode($encoded);
$this->userAvatarManager->replaceBase64($user, $data->data, $data->name);
}
@@ -65,12 +75,8 @@ class UserProfileController extends Controller
public function updateSettings(UpdateSettingsRequest $request): JsonResponse
{
$user = $request->user();
$request->user()->setSettings($request->settings);
$user->setSettings($request->settings);
return response()->json([
'success' => true,
]);
return response()->json(['success' => true]);
}
}
+8 -5
View File
@@ -13,7 +13,7 @@ use Illuminate\Support\Facades\Route;
Route::get('/invitations/pending', [InvitationResponseController::class, 'pending']);
Route::post('/invitations/{invitation:token}/accept', [InvitationResponseController::class, 'accept']);
Route::post('/invitations/{invitation:token}/decline', [InvitationResponseController::class, 'decline']);
Route::get('/auth/check', [AuthController::class, 'check']);
Route::get('auth/check', [AuthController::class, 'check']); // is the caller's token still live?
Route::apiResource('company-invitations', InvitationController::class)->only(['index', 'store', 'destroy']);
@@ -23,11 +23,14 @@ Route::get('/me/settings', [UserProfileController::class, 'showSettings']);
Route::put('/me/settings', [UserProfileController::class, 'updateSettings']);
Route::post('/me/upload-avatar', [UserProfileController::class, 'uploadAvatar']);
Route::put('/company', [CompanyController::class, 'updateCompany']);
Route::post('/company/upload-logo', [CompanyController::class, 'uploadCompanyLogo']);
Route::controller(CompanyController::class)->group(function (): void {
// The company record itself; its settings are handled separately below.
Route::put('company', 'updateCompany');
Route::post('company/upload-logo', 'uploadCompanyLogo');
});
Route::get('/company/settings', [CompanySettingsController::class, 'show']);
Route::post('/company/settings', [CompanySettingsController::class, 'update']);
Route::get('/company/has-transactions', [CompanySettingsController::class, 'checkTransactions']);
Route::get('abilities', AbilitiesController::class);
Route::apiResource('roles', RolesController::class);
Route::get('abilities', AbilitiesController::class); // the fixed catalogue the role editor lists
Route::apiResources(['roles' => RolesController::class]);
+2 -2
View File
@@ -5,9 +5,9 @@ use App\Domains\Accounts\Http\Controllers\Company\CompanySettingsController;
use App\Domains\Accounts\Http\Controllers\Company\MembersController;
use Illuminate\Support\Facades\Route;
Route::post('companies', [CompaniesController::class, 'store']);
Route::post('companies', [CompaniesController::class, 'store']); // gated on owning the active company
Route::post('/transfer/ownership/{user}', [CompanySettingsController::class, 'transferOwnership']);
Route::post('companies/delete', [CompaniesController::class, 'destroy']);
Route::post('companies/delete', [CompaniesController::class, 'destroy']); // posted, and confirmed by name
Route::get('companies', [CompaniesController::class, 'userCompanies']);
Route::post('/members/delete', [MembersController::class, 'delete']);
+2 -2
View File
@@ -8,9 +8,9 @@ use Illuminate\Support\Facades\Route;
Route::prefix('auth')->group(function (): void {
Route::post('login', [AuthController::class, 'login']);
Route::post('logout', [AuthController::class, 'logout'])->middleware('auth:sanctum');
Route::post('logout', [AuthController::class, 'logout'])->middleware(['auth:sanctum']); // needs the token it revokes
Route::post('password/email', [ForgotPasswordController::class, 'sendResetLinkEmail'])->middleware('throttle:10,2');
Route::post('reset/password', [ResetPasswordController::class, 'reset']);
Route::post('/reset/password', [ResetPasswordController::class, 'reset']); // the mailed token travels in the body
});
Route::get('/invitations/{token}/details', [InvitationRegistrationController::class, 'details']);