Architecture Multi-Clients
Architecture
Chaque client dispose de :
- Sa propre API Laravel
- Sa propre base de données
- Son propre domaine
Authentification JWT
Configuration
// config/jwt.php
return [
'ttl' => env('JWT_TTL', 10080), // Durée de validité en minutes
'refresh_ttl' => env('JWT_REFRESH_TTL', 20160), // 2 semaines
'secret' => env('JWT_SECRET'),
];
// app/Models/User.php
use Tymon\JWTAuth\Contracts\JWTSubject;
class User extends Authenticatable implements JWTSubject
{
public function getJWTIdentifier()
{
return $this->getKey();
}
public function getJWTCustomClaims()
{
return [];
}
}
Routes API
Structure des Routes
// routes/api.php
Route::get('/client-logo', [AuthController::class, 'getClientLogo']);
Route::post('/vue/login', [AuthController::class, 'login']);
Route::middleware('auth:api')->group(function () {
// Auth
Route::post('/vue/logout', [AuthController::class, 'logout']);
Route::get('/vue/me', [AuthController::class, 'me']);
Route::get('/verifyToken', [AuthController::class, 'verifyToken']);
// Interventions
Route::get('/getUserInterventions', [InterventionController::class, 'getUserInterventions']);
Route::get('/getInterventionCountsByMonth', [InterventionController::class, 'getInterventionCountsByMonth']);
Route::post('/update_intervention_status', [InterventionController::class, 'update_intervention_status'])->name("update_intervention_status");
Route::get('/getSiteFiles', [InterventionController::class, 'getSiteFiles'])->name("getSiteFiles");
Route::post('/save_bi_intervention_tech', [InterventionController::class, 'save_bi_intervention_tech'])->name("save_bi_intervention_tech");
// Fichiers
Route::get('/getFile/{type}/{id}', [FileController::class, 'getFile'])->name("getFile");
Route::post('/uploadFile', [FileController::class, 'uploadFile'])->name("uploadFile");
Route::get('/getCategories', [FileController::class, 'getCategories'])->name("getCategories");
Route::delete('/deleteFile/{id}', [FileController::class, 'destroyFile'])->name("deleteFile");
// Messages
Route::get('/getInterventionMessages', [InterventionController::class, 'getInterventionMessages'])->name("getInterventionMessages");
Route::post('/addMessageIntervention', [InterventionController::class, 'addMessageIntervention'])->name("addMessageIntervention");
// Pour les select options
Route::get('/getCustomers', [InterventionController::class, 'getCustomers'])->name("getCustomers");
Route::get('/getActivities', [InterventionController::class, 'getActivities'])->name("getActivities");
// Notifications
Route::post('/markAsRead/{id}', [NotificationsController::class, 'markAsRead'])->name("markAsRead");
Route::get('/notifications-stream', [NotificationsController::class, 'stream'])->name('notifications.stream');
});
Controllers
// app/Http/Controllers/API/InterventionController.php
public function getInterventionCountsByMonth(Request $request)
{
$user = JWTAuth::parseToken()->authenticate();
$query = InterventionDetail::where('use_idAssigned', $user->id)
->whereNotNull('intDet_planStartDate')
->selectRaw('DATE(intDet_planStartDate) as date, COUNT(*) as count')
->groupBy('date')
->orderBy('date')
->get();
$result = [];
foreach ($query as $item) {
$result[$item->date] = $item->count;
}
return response()->json($result);
}
...
...
Configuration CORS
// config/cors.php
return [
'paths' => ['api/*'],
'allowed_methods' => ['*'],
'allowed_origins' => ['*'],
'allowed_origins_patterns' => [],
'allowed_headers' => ['*'],
'exposed_headers' => [],
'max_age' => 0,
'supports_credentials' => true,
];