638 lines
25 KiB
PHP
638 lines
25 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers\Api;
|
|
|
|
use App\Http\Controllers\Controller;
|
|
use App\Models\Worker;
|
|
use App\Models\WorkerCategory;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\Cache;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Facades\Hash;
|
|
use Illuminate\Support\Facades\Mail;
|
|
use Illuminate\Support\Facades\Validator;
|
|
use Illuminate\Support\Str;
|
|
|
|
class WorkerAuthController extends Controller
|
|
{
|
|
/**
|
|
* Step 1: Send OTP to worker's mobile number or email.
|
|
* Accepts: phone OR email (one is required).
|
|
*/
|
|
public function sendOtp(Request $request)
|
|
{
|
|
$validator = Validator::make($request->all(), [
|
|
'phone' => 'required_without:email|nullable|string|max:50',
|
|
'email' => 'required_without:phone|nullable|email|max:255',
|
|
], [
|
|
'phone.required_without' => 'Please provide a mobile number or email address.',
|
|
'email.required_without' => 'Please provide a mobile number or email address.',
|
|
]);
|
|
|
|
if ($validator->fails()) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'Validation error.',
|
|
'errors' => $validator->errors()
|
|
], 422);
|
|
}
|
|
|
|
// Determine identifier (phone takes priority)
|
|
$identifier = $request->phone ?? $request->email;
|
|
$isPhone = !empty($request->phone);
|
|
|
|
// Generate OTP (always 111111 in local/dev, random in production)
|
|
$otp = app()->environment('production') ? strval(rand(100000, 999999)) : '111111';
|
|
|
|
// Cache OTP for 10 minutes
|
|
Cache::put('worker_otp_' . $identifier, [
|
|
'code' => $otp,
|
|
'expires_at' => now()->addMinutes(10),
|
|
], 600);
|
|
|
|
// Send OTP via email if email provided (phone SMS would be handled by SMS gateway)
|
|
if (!$isPhone && $request->email) {
|
|
try {
|
|
Mail::raw("Your Migrant Worker verification code is: {$otp}\n\nThis code expires in 10 minutes.", function ($m) use ($request, $otp) {
|
|
$m->to($request->email)
|
|
->subject('Your Migrant Worker Verification Code');
|
|
});
|
|
} catch (\Exception $mailEx) {
|
|
logger()->error('Worker OTP Mail Failure: ' . $mailEx->getMessage());
|
|
}
|
|
}
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'message' => $isPhone
|
|
? 'OTP sent to your mobile number.'
|
|
: 'OTP sent to your email address.',
|
|
'identifier' => $identifier,
|
|
], 200);
|
|
}
|
|
|
|
/**
|
|
* Step 2: Verify OTP sent to mobile/email.
|
|
* Accepts: phone OR email, otp.
|
|
*/
|
|
public function verifyOtp(Request $request)
|
|
{
|
|
$validator = Validator::make($request->all(), [
|
|
'phone' => 'required_without:email|nullable|string|max:50',
|
|
'email' => 'required_without:phone|nullable|email|max:255',
|
|
'otp' => 'required|string|size:6',
|
|
]);
|
|
|
|
if ($validator->fails()) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'errors' => $validator->errors()
|
|
], 422);
|
|
}
|
|
|
|
$identifier = $request->phone ?? $request->email;
|
|
$cachedData = Cache::get('worker_otp_' . $identifier);
|
|
|
|
if (!$cachedData || $cachedData['code'] !== $request->otp || now()->gt($cachedData['expires_at'])) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'Invalid or expired verification code.'
|
|
], 400);
|
|
}
|
|
|
|
// Mark OTP as verified in cache (used as gate for profile setup step)
|
|
Cache::put('worker_otp_verified_' . $identifier, true, 3600); // 1 hour grace
|
|
Cache::forget('worker_otp_' . $identifier);
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'message' => 'Mobile number verified successfully. Please complete your profile.',
|
|
'identifier' => $identifier,
|
|
], 200);
|
|
}
|
|
|
|
/**
|
|
* Step 3: Complete basic profile setup after OTP verification.
|
|
* Accepts: phone OR email, name, nationality, skills (array of skill IDs).
|
|
*/
|
|
public function setupProfile(Request $request)
|
|
{
|
|
$validator = Validator::make($request->all(), [
|
|
'phone' => 'required_without:email|nullable|string|max:50',
|
|
'email' => 'required_without:phone|nullable|email|max:255',
|
|
'name' => 'required|string|max:255',
|
|
'nationality' => 'required|string|max:100',
|
|
'skills' => 'nullable|array',
|
|
'skills.*' => 'integer|exists:skills,id',
|
|
]);
|
|
|
|
if ($validator->fails()) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'errors' => $validator->errors()
|
|
], 422);
|
|
}
|
|
|
|
$identifier = $request->phone ?? $request->email;
|
|
|
|
// Ensure OTP was verified in this session
|
|
if (!Cache::get('worker_otp_verified_' . $identifier)) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'OTP verification is required before profile setup.'
|
|
], 403);
|
|
}
|
|
|
|
// Check if already registered
|
|
if ($request->phone && Worker::where('phone', $request->phone)->exists()) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'This mobile number is already registered.'
|
|
], 409);
|
|
}
|
|
if ($request->email && Worker::where('email', $request->email)->exists()) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'This email address is already registered.'
|
|
], 409);
|
|
}
|
|
|
|
try {
|
|
// Build email for workers who registered by phone
|
|
$email = $request->email;
|
|
if (!$email && $request->phone) {
|
|
$phoneClean = preg_replace('/[^0-9]/', '', $request->phone);
|
|
$email = "worker.{$phoneClean}@migrant.ae";
|
|
if (Worker::where('email', $email)->exists()) {
|
|
$email = "worker.{$phoneClean}." . rand(10, 99) . "@migrant.ae";
|
|
}
|
|
}
|
|
|
|
$apiToken = Str::random(80);
|
|
|
|
$worker = DB::transaction(function () use ($request, $email, $apiToken) {
|
|
$worker = Worker::create([
|
|
'name' => $request->name,
|
|
'email' => $email,
|
|
'phone' => $request->phone ?? null,
|
|
'password' => Hash::make(Str::random(16)), // No password at this stage
|
|
'nationality' => $request->nationality,
|
|
'age' => 25, // Default — updated later in full profile
|
|
'salary' => 1500, // Default AED — updated later
|
|
'availability'=> 'Immediate',
|
|
'experience' => 'Not Specified',
|
|
'religion' => 'Not Specified',
|
|
'bio' => 'New worker on Migrant platform.',
|
|
'category_id' => 7, // Default: General Helper
|
|
'verified' => false,
|
|
'status' => 'active',
|
|
'api_token' => $apiToken,
|
|
]);
|
|
|
|
// Sync skills if provided
|
|
if (!empty($request->skills)) {
|
|
$worker->skills()->sync($request->skills);
|
|
}
|
|
|
|
return $worker;
|
|
});
|
|
|
|
// Clear the OTP verification gate
|
|
Cache::forget('worker_otp_verified_' . $identifier);
|
|
|
|
$worker->load(['category', 'skills']);
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'message' => 'Registration complete! Welcome to Migrant.',
|
|
'data' => [
|
|
'worker' => $worker,
|
|
'token' => $apiToken,
|
|
]
|
|
], 201);
|
|
|
|
} catch (\Exception $e) {
|
|
logger()->error('Worker Profile Setup Failure: ' . $e->getMessage());
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'Registration failed. Please try again.',
|
|
'error' => app()->environment('local') ? $e->getMessage() : 'Internal Server Error'
|
|
], 500);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Legacy unified registration — kept for backward compatibility.
|
|
*/
|
|
public function register(Request $request)
|
|
{
|
|
$validator = Validator::make($request->all(), [
|
|
'name' => 'required|string|max:255',
|
|
'phone' => 'required|string|max:50|unique:workers,phone',
|
|
'salary' => 'required|numeric|min:0',
|
|
'password' => 'required|string|min:6',
|
|
'passport_file' => 'required|file|mimes:jpeg,png,jpg,pdf|max:10240',
|
|
'skills' => 'nullable',
|
|
'visa_file' => 'nullable|file|mimes:jpeg,png,jpg,pdf|max:10240',
|
|
]);
|
|
|
|
if ($validator->fails()) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'Validation error.',
|
|
'errors' => $validator->errors()
|
|
], 422);
|
|
}
|
|
|
|
try {
|
|
$phoneClean = preg_replace('/[^0-9]/', '', $request->phone);
|
|
$email = "worker.{$phoneClean}@migrant.ae";
|
|
if (Worker::where('email', $email)->exists()) {
|
|
$email = "worker.{$phoneClean}." . rand(10, 99) . "@migrant.ae";
|
|
}
|
|
|
|
$skillsArray = [];
|
|
if ($request->has('skills')) {
|
|
$skillsInput = $request->skills;
|
|
if (is_array($skillsInput)) {
|
|
$skillsArray = $skillsInput;
|
|
} elseif (is_string($skillsInput)) {
|
|
$decoded = json_decode($skillsInput, true);
|
|
$skillsArray = (json_last_error() === JSON_ERROR_NONE && is_array($decoded))
|
|
? $decoded
|
|
: array_filter(array_map('trim', explode(',', $skillsInput)));
|
|
}
|
|
}
|
|
|
|
$destinationPath = public_path('uploads/documents');
|
|
if (!file_exists($destinationPath)) {
|
|
mkdir($destinationPath, 0755, true);
|
|
}
|
|
|
|
$passportPath = null;
|
|
$visaPath = null;
|
|
|
|
if ($request->hasFile('passport_file')) {
|
|
$file = $request->file('passport_file');
|
|
$fileName = time() . '_passport_' . preg_replace('/[^a-zA-Z0-9_.-]/', '', $file->getClientOriginalName());
|
|
$file->move($destinationPath, $fileName);
|
|
$passportPath = 'uploads/documents/' . $fileName;
|
|
}
|
|
|
|
if ($request->hasFile('visa_file')) {
|
|
$file = $request->file('visa_file');
|
|
$fileName = time() . '_visa_' . preg_replace('/[^a-zA-Z0-9_.-]/', '', $file->getClientOriginalName());
|
|
$file->move($destinationPath, $fileName);
|
|
$visaPath = 'uploads/documents/' . $fileName;
|
|
}
|
|
|
|
$apiToken = Str::random(80);
|
|
|
|
$result = DB::transaction(function () use ($request, $email, $skillsArray, $passportPath, $visaPath, $apiToken) {
|
|
$worker = Worker::create([
|
|
'name' => $request->name,
|
|
'email' => $email,
|
|
'phone' => $request->phone,
|
|
'password' => $request->password,
|
|
'nationality' => 'Not Specified',
|
|
'age' => 25,
|
|
'salary' => $request->salary,
|
|
'availability'=> 'Immediate',
|
|
'experience' => 'Not Specified',
|
|
'religion' => 'Not Specified',
|
|
'bio' => 'Hardworking and reliable General Helper available for immediate hire.',
|
|
'category_id' => 7,
|
|
'verified' => false,
|
|
'status' => 'active',
|
|
'api_token' => $apiToken,
|
|
]);
|
|
|
|
if (!empty($skillsArray)) {
|
|
$worker->skills()->sync($skillsArray);
|
|
}
|
|
|
|
$worker->documents()->create([
|
|
'type' => 'passport',
|
|
'number' => 'P' . rand(100000, 999999),
|
|
'issue_date' => now()->subYears(2)->toDateString(),
|
|
'expiry_date' => now()->addYears(8)->toDateString(),
|
|
'ocr_accuracy'=> 98.5,
|
|
'file_path' => $passportPath,
|
|
]);
|
|
|
|
$worker->documents()->create([
|
|
'type' => 'visa',
|
|
'number' => 'V' . rand(100000, 999999),
|
|
'issue_date' => now()->subYear()->toDateString(),
|
|
'expiry_date' => now()->addYears(2)->toDateString(),
|
|
'ocr_accuracy'=> $visaPath ? 98.5 : 0.0,
|
|
'file_path' => $visaPath,
|
|
]);
|
|
|
|
return $worker;
|
|
});
|
|
|
|
$result->load(['category', 'skills', 'documents']);
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'message' => 'Worker registered and authenticated successfully.',
|
|
'data' => ['worker' => $result, 'token' => $apiToken]
|
|
], 201);
|
|
|
|
} catch (\Exception $e) {
|
|
logger()->error('Worker Registration Failure: ' . $e->getMessage());
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'An error occurred during worker registration. Please try again.',
|
|
'error' => app()->environment('local') ? $e->getMessage() : 'Internal Server Error'
|
|
], 500);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Authenticate a worker and return a secure Bearer token.
|
|
*/
|
|
public function login(Request $request)
|
|
{
|
|
$validator = Validator::make($request->all(), [
|
|
'phone' => 'required_without:email|nullable|string',
|
|
'email' => 'required_without:phone|nullable|email',
|
|
'password' => 'required|string',
|
|
]);
|
|
|
|
if ($validator->fails()) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'Validation error.',
|
|
'errors' => $validator->errors()
|
|
], 422);
|
|
}
|
|
|
|
try {
|
|
$worker = $request->phone
|
|
? Worker::where('phone', $request->phone)->first()
|
|
: Worker::where('email', $request->email)->first();
|
|
|
|
if (!$worker || !Hash::check($request->password, $worker->password)) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'Invalid credentials.'
|
|
], 401);
|
|
}
|
|
|
|
$apiToken = Str::random(80);
|
|
$worker->update(['api_token' => $apiToken]);
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'message' => 'Worker logged in successfully.',
|
|
'data' => [
|
|
'worker' => $worker->load(['category', 'skills', 'documents']),
|
|
'token' => $apiToken
|
|
]
|
|
], 200);
|
|
|
|
} catch (\Exception $e) {
|
|
logger()->error('Worker Login Failure: ' . $e->getMessage());
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'An error occurred during login. Please try again.',
|
|
'error' => app()->environment('local') ? $e->getMessage() : 'Internal Server Error'
|
|
], 500);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
class WorkerAuthController extends Controller
|
|
{
|
|
/**
|
|
* Register a new worker via Mobile App API (Single Unified Multipart Request).
|
|
*
|
|
* Accepts: name, phone, salary, password, skills, passport_file (REQUIRED), and visa_file.
|
|
* All other fields are generated with backend defaults.
|
|
*
|
|
* @param \Illuminate\Http\Request $request
|
|
* @return \Illuminate\Http\JsonResponse
|
|
*/
|
|
public function register(Request $request)
|
|
{
|
|
// 1. Validate the unified request
|
|
$validator = Validator::make($request->all(), [
|
|
'name' => 'required|string|max:255',
|
|
'phone' => 'required|string|max:50|unique:workers,phone',
|
|
'salary' => 'required|numeric|min:0',
|
|
'password' => 'required|string|min:6', // Password is required
|
|
'passport_file' => 'required|file|mimes:jpeg,png,jpg,pdf|max:10240', // STRICTLY REQUIRED (Max 10MB)
|
|
'skills' => 'nullable', // Handled dynamically in controller (JSON, Array, or Comma-separated)
|
|
'visa_file' => 'nullable|file|mimes:jpeg,png,jpg,pdf|max:10240', // Optional (Max 10MB)
|
|
], [
|
|
'phone.unique' => 'This mobile number is already registered.',
|
|
'password.min' => 'The password must be at least 6 characters long.',
|
|
'passport_file.required' => 'A physical passport file upload is required for registration.',
|
|
'passport_file.mimes' => 'The passport document must be an image (jpg, jpeg, png) or a PDF.',
|
|
'passport_file.max' => 'The passport document must not exceed 10MB.',
|
|
'visa_file.mimes' => 'The visa document must be an image (jpg, jpeg, png) or a PDF.',
|
|
'visa_file.max' => 'The visa document must not exceed 10MB.',
|
|
]);
|
|
|
|
if ($validator->fails()) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'Validation error.',
|
|
'errors' => $validator->errors()
|
|
], 422);
|
|
}
|
|
|
|
try {
|
|
// 2. Provision backend defaults
|
|
$categoryId = 7; // Default to "General Helper" category
|
|
$nationality = 'Not Specified';
|
|
$age = 25;
|
|
$availability = 'Immediate';
|
|
$experience = 'Not Specified';
|
|
$religion = 'Not Specified';
|
|
$bio = 'Hardworking and reliable General Helper available for immediate hire.';
|
|
|
|
// Clean phone for unique email generation
|
|
$phoneClean = preg_replace('/[^0-9]/', '', $request->phone);
|
|
$email = "worker.{$phoneClean}@migrant.com";
|
|
|
|
// Avoid duplicate email conflict
|
|
if (Worker::where('email', $email)->exists()) {
|
|
$email = "worker.{$phoneClean}." . rand(10, 99) . "@migrant.com";
|
|
}
|
|
|
|
// Parse skills robustly for multipart form variants
|
|
$skillsArray = [];
|
|
if ($request->has('skills')) {
|
|
$skillsInput = $request->skills;
|
|
if (is_array($skillsInput)) {
|
|
$skillsArray = $skillsInput;
|
|
} elseif (is_string($skillsInput)) {
|
|
$decoded = json_decode($skillsInput, true);
|
|
if (json_last_error() === JSON_ERROR_NONE && is_array($decoded)) {
|
|
$skillsArray = $decoded;
|
|
} else {
|
|
// Fallback: comma separated e.g. "6,8"
|
|
$skillsArray = array_filter(array_map('trim', explode(',', $skillsInput)));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Handle file uploads securely
|
|
$passportPath = null;
|
|
$visaPath = null;
|
|
|
|
// Ensure destination directory exists
|
|
$destinationPath = public_path('uploads/documents');
|
|
if (!file_exists($destinationPath)) {
|
|
mkdir($destinationPath, 0755, true);
|
|
}
|
|
|
|
if ($request->hasFile('passport_file')) {
|
|
$file = $request->file('passport_file');
|
|
$fileName = time() . '_passport_' . preg_replace('/[^a-zA-Z0-9_.-]/', '', $file->getClientOriginalName());
|
|
$file->move($destinationPath, $fileName);
|
|
$passportPath = 'uploads/documents/' . $fileName;
|
|
}
|
|
|
|
if ($request->hasFile('visa_file')) {
|
|
$file = $request->file('visa_file');
|
|
$fileName = time() . '_visa_' . preg_replace('/[^a-zA-Z0-9_.-]/', '', $file->getClientOriginalName());
|
|
$file->move($destinationPath, $fileName);
|
|
$visaPath = 'uploads/documents/' . $fileName;
|
|
}
|
|
|
|
// Generate initial API token
|
|
$apiToken = Str::random(80);
|
|
|
|
// 3. Save Worker and Document records inside database transaction
|
|
$result = DB::transaction(function () use ($request, $categoryId, $email, $nationality, $age, $availability, $experience, $religion, $bio, $skillsArray, $passportPath, $visaPath, $apiToken) {
|
|
|
|
$worker = Worker::create([
|
|
'name' => $request->name,
|
|
'email' => $email,
|
|
'phone' => $request->phone,
|
|
'password' => $request->password, // Will cast to hashed auto-magically
|
|
'nationality' => $nationality,
|
|
'age' => $age,
|
|
'salary' => $request->salary,
|
|
'availability' => $availability,
|
|
'experience' => $experience,
|
|
'religion' => $religion,
|
|
'bio' => $bio,
|
|
'category_id' => $categoryId,
|
|
'verified' => false,
|
|
'status' => 'active',
|
|
'api_token' => $apiToken,
|
|
]);
|
|
|
|
// Sync selected skills
|
|
if (!empty($skillsArray)) {
|
|
$worker->skills()->sync($skillsArray);
|
|
}
|
|
|
|
// Provision Passport document record (User uploaded REQUIRED file)
|
|
$worker->documents()->create([
|
|
'type' => 'passport',
|
|
'number' => 'P' . rand(100000, 999999),
|
|
'issue_date' => now()->subYears(2)->toDateString(),
|
|
'expiry_date' => now()->addYears(8)->toDateString(),
|
|
'ocr_accuracy' => 98.5,
|
|
'file_path' => $passportPath,
|
|
]);
|
|
|
|
// Provision Visa document record (User uploaded OR mock scaffolding)
|
|
$worker->documents()->create([
|
|
'type' => 'visa',
|
|
'number' => 'V' . rand(100000, 999999),
|
|
'issue_date' => now()->subYear()->toDateString(),
|
|
'expiry_date' => now()->addYears(2)->toDateString(),
|
|
'ocr_accuracy' => $visaPath ? 98.5 : 0.0,
|
|
'file_path' => $visaPath,
|
|
]);
|
|
|
|
return $worker;
|
|
});
|
|
|
|
// Load relations for response
|
|
$result->load(['category', 'skills', 'documents']);
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'message' => 'Worker registered and authenticated successfully.',
|
|
'data' => [
|
|
'worker' => $result,
|
|
'token' => $apiToken
|
|
]
|
|
], 201);
|
|
|
|
} catch (\Exception $e) {
|
|
logger()->error('Mobile Unified Worker Registration Failure: ' . $e->getMessage());
|
|
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'An error occurred during worker registration. Please try again.',
|
|
'error' => app()->environment('local') ? $e->getMessage() : 'Internal Server Error'
|
|
], 500);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Authenticate a worker and return a secure Bearer token.
|
|
*
|
|
* @param \Illuminate\Http\Request $request
|
|
* @return \Illuminate\Http\JsonResponse
|
|
*/
|
|
public function login(Request $request)
|
|
{
|
|
$validator = Validator::make($request->all(), [
|
|
'phone' => 'required|string',
|
|
'password' => 'required|string',
|
|
]);
|
|
|
|
if ($validator->fails()) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'Validation error.',
|
|
'errors' => $validator->errors()
|
|
], 422);
|
|
}
|
|
|
|
try {
|
|
$worker = Worker::where('phone', $request->phone)->first();
|
|
|
|
if (!$worker || !Hash::check($request->password, $worker->password)) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'Invalid mobile number or password.'
|
|
], 401);
|
|
}
|
|
|
|
// Generate and assign a fresh API token
|
|
$apiToken = Str::random(80);
|
|
$worker->update(['api_token' => $apiToken]);
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'message' => 'Worker logged in successfully.',
|
|
'data' => [
|
|
'worker' => $worker->load(['category', 'skills', 'documents']),
|
|
'token' => $apiToken
|
|
]
|
|
], 200);
|
|
|
|
} catch (\Exception $e) {
|
|
logger()->error('Mobile Worker Login Failure: ' . $e->getMessage());
|
|
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'An error occurred during login. Please try again.',
|
|
'error' => app()->environment('local') ? $e->getMessage() : 'Internal Server Error'
|
|
], 500);
|
|
}
|
|
}
|
|
}
|