mandant-crm/backend/routes/api.php
Tim Stollberg 71730fef06 feat: remove Filament, add user account management
- Remove Filament /admin panel completely (AdminPanelProvider, UserResource, routes)
- Update composer.json to remove filament/filament dependency
- Add User model soft deletes with SoftDeletes trait + migration
- Strengthen UserPolicy::delete to prevent deleting last remaining admin
- Build out User module following pattern: CreateUserAction, UpdateUserAction, GetUsersAction,
  UserResource, UserController, Create/UpdateUserRequest, UserData DTO
- Add /api/users routes (index, store, update, destroy), all admin-gated via policy
- Create frontend Settings > Accounts page (settings/accounts.vue) for admin user management
- Add useUsers composable mirroring useClient pattern
- Add "Benutzer" link to settings dropdown in clients/index.vue
- All tests pass; User soft-delete and last-admin protection verified

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-07-13 13:05:40 +07:00

70 lines
3.2 KiB
PHP

<?php
use App\Modules\Client\Controllers\ClientController;
use App\Modules\CustomField\Controllers\CustomFieldDefinitionController;
use App\Modules\CustomField\Controllers\CustomFieldValueController;
use App\Modules\History\Controllers\HistoryController;
use App\Modules\History\Controllers\HistoryTypeController;
use App\Modules\Import\Controllers\ClientImportController;
use App\Modules\Search\Controllers\SearchController;
use App\Modules\User\Controllers\UserController;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
Route::post('/login', function (Request $request) {
$credentials = $request->validate([
'email' => ['required', 'email'],
'password' => ['required'],
]);
if (! auth()->attempt($credentials)) {
return response()->json(['message' => 'Invalid credentials'], 401);
}
if ($request->hasSession()) {
$request->session()->regenerate();
}
return response()->json(['data' => auth()->user()]);
});
Route::middleware('auth:sanctum')->group(function () {
Route::apiResource('users', UserController::class)->only(['index', 'store', 'update', 'destroy']);
Route::post('/logout', function (Request $request) {
// SPA sessions authenticate through the stateful `web` guard; the default
// `sanctum` guard is a RequestGuard with no logout(), so call it explicitly.
auth()->guard('web')->logout();
if ($request->hasSession()) {
$request->session()->invalidate();
$request->session()->regenerateToken();
}
return response()->json(null, 204);
});
Route::get('/user', fn (Request $request) => response()->json(['data' => $request->user()]));
Route::get('search', SearchController::class);
Route::post('clients/import', [ClientImportController::class, 'store']);
Route::apiResource('clients', ClientController::class);
Route::get('clients/{client}/history', [HistoryController::class, 'index']);
Route::post('clients/{client}/history', [HistoryController::class, 'store']);
Route::put('history/{historyEntry}', [HistoryController::class, 'update']);
Route::delete('history/{historyEntry}', [HistoryController::class, 'destroy']);
Route::get('history-types', [HistoryTypeController::class, 'index']);
Route::post('history-types', [HistoryTypeController::class, 'store']);
Route::post('history-types/reorder', [HistoryTypeController::class, 'reorder']);
Route::put('history-types/{historyType}', [HistoryTypeController::class, 'update']);
Route::delete('history-types/{historyType}', [HistoryTypeController::class, 'destroy']);
Route::get('custom-fields', [CustomFieldDefinitionController::class, 'index']);
Route::post('custom-fields', [CustomFieldDefinitionController::class, 'store']);
Route::post('custom-fields/reorder', [CustomFieldDefinitionController::class, 'reorder']);
Route::put('custom-fields/{customFieldDefinition}', [CustomFieldDefinitionController::class, 'update']);
Route::delete('custom-fields/{customFieldDefinition}', [CustomFieldDefinitionController::class, 'destroy']);
Route::put('clients/{client}/custom-fields/{customFieldDefinition}', [CustomFieldValueController::class, 'update']);
});