validate([ 'email' => 'required|email', 'password' => 'required', 'device_name' => 'nullable|string', 'subdomain' => 'nullable|string', ]); // 1. Resolve Tenant Context (Host -> Header -> Body -> Email Auto-Discovery) $host = $request->getHost(); $centralDomains = config('tenancy.central_domains', ['127.0.0.1', 'localhost', 'hrm.test']); $tenantId = null; if (!in_array($host, $centralDomains, true)) { $domain = \Stancl\Tenancy\Database\Models\Domain::where('domain', $host)->first(); if ($domain) { $tenantId = $domain->tenant_id; } } if (!$tenantId) { $tenantId = $request->header('X-Tenant') ?? $request->header('X-Subdomain') ?? $request->subdomain ?? $request->tenant; } // Auto-Discover Tenant by User Email if no tenant header or subdomain provided if (!$tenantId && $request->filled('email')) { $inquiry = \App\Models\Inquiry::on('sqlite')->where('email', $request->email)->first(); if ($inquiry) { $tenantId = $inquiry->desired_subdomain ?? $inquiry->domain_prefix; } else { // Check central tenants table by email $tenantRecord = \App\Models\Tenant::on('sqlite')->where('email', $request->email)->first(); if ($tenantRecord) { $tenantId = $tenantRecord->id; } } } if ($tenantId && class_exists('\App\Models\Tenant')) { $tenant = \App\Models\Tenant::find($tenantId); if (!$tenant) { $domainMatch = \Stancl\Tenancy\Database\Models\Domain::where('domain', $tenantId)->first(); $tenant = $domainMatch ? $domainMatch->tenant : null; } if ($tenant) { if (function_exists('tenancy') && tenancy()->initialized) { tenancy()->end(); } tenancy()->initialize($tenant); } } // Authenticate User in Current Context (Tenant DB or Central DB) $user = User::where('email', $request->email)->first(); // Fallback for central users (e.g. superadmin) logging in on central host if (!$user && function_exists('tenant') && tenant()) { $centralUser = User::on('sqlite')->where('email', $request->email)->first(); if ($centralUser && in_array($centralUser->type, ['superadmin'])) { if (function_exists('tenancy') && tenancy()->initialized) { tenancy()->end(); } $user = $centralUser; } } $user = User::where('email', $request->email)->first(); // Enforce Subdomain & Tenant Domain Access Isolation for API/Mobile Auth if (function_exists('tenant') && tenant()) { $currentTenantId = tenant('id'); $tenantInquiry = \App\Models\Inquiry::on('sqlite')->where('desired_subdomain', $currentTenantId)->first(); $allowedEmail = $tenantInquiry ? $tenantInquiry->email : tenant('email'); if ($user && $user->type === 'company' && $allowedEmail && strtolower($user->email) !== strtolower($allowedEmail)) { return response()->json([ 'success' => false, 'data' => null, 'message' => 'This account does not have administrator access to this subdomain.', 'errors' => ['email' => ['This account does not have administrator access to this subdomain.']], ], 401); } } if (!$user || !Hash::check($request->password, $user->password)) { return response()->json([ 'success' => false, 'data' => null, 'message' => 'Invalid email or password', 'errors' => ['email' => ['Invalid email or password']], ], 401); } if ($user->is_enable_login == 0 || $user->status === 'inactive') { return response()->json([ 'success' => false, 'data' => null, 'message' => 'Your account login is disabled', 'errors' => null, ], 403); } // Check if Mobile App Access module is enabled for this tenant subdomain if (function_exists('tenant') && tenant()) { $isMobileEnabled = \App\Models\TenantModule::where('module_key', 'mobile')->where('enabled', true)->exists(); if (!$isMobileEnabled) { return response()->json([ 'success' => false, 'data' => null, 'message' => 'Mobile App access is not enabled for your company subscription.', 'errors' => ['mobile' => ['Mobile App access is not enabled for your company subscription.']], ], 403); } } $deviceName = $request->device_name ?? 'mobile_app'; $token = $user->createToken($deviceName)->plainTextToken; if (\Illuminate\Support\Facades\Schema::hasTable('employees')) { $user->load('employee'); } if (!empty($user->avatar)) { $user->avatar = get_file($user->avatar); } // Resolve enabled modules map for mobile client app $enabledModules = []; if (function_exists('tenant') && tenant()) { $enabledModules = \App\Models\TenantModule::pluck('enabled', 'module_key')->map(fn($v) => (bool)$v)->toArray(); } return response()->json([ 'success' => true, 'token' => $token, 'user' => $user, 'tenant_modules' => $enabledModules, 'data' => [ 'token' => $token, 'user' => $user, 'tenant_modules' => $enabledModules, ], 'message' => 'Login successful', 'errors' => null, ]); } public function logout(Request $request) { if ($request->user() && $request->user()->currentAccessToken()) { $request->user()->currentAccessToken()->delete(); } return response()->json([ 'success' => true, 'data' => null, 'message' => 'Successfully logged out', 'errors' => null, ]); } public function user(Request $request) { $user = $request->user()->load('employee'); if (!empty($user->avatar)) { $user->avatar = get_file($user->avatar); } if ($user->employee && !empty($user->employee->avatar)) { $user->employee->avatar = get_file($user->employee->avatar); } return response()->json([ 'success' => true, 'user' => $user, 'data' => [ 'id' => $user->id, 'name' => $user->name, 'email' => $user->email, 'avatar' => $user->avatar, 'user' => $user, 'employee' => $user->employee, ], 'message' => null, 'errors' => null, ]); } public function updateProfile(Request $request) { $user = $request->user(); $request->validate([ 'name' => 'nullable|string|max:255', 'email' => 'nullable|email|unique:users,email,' . $user->id, 'avatar' => 'nullable|image|mimes:jpeg,png,jpg,webp|max:5120', ]); if ($request->filled('name')) { $user->name = $request->name; } if ($request->filled('email')) { $user->email = $request->email; } if ($request->hasFile('avatar')) { $file = $request->file('avatar'); $filename = time() . '_' . uniqid() . '.' . $file->getClientOriginalExtension(); $file->storeAs('avatars', $filename, 'public'); $user->avatar = 'avatars/' . $filename; if ($user->employee && \Schema::hasColumn('employees', 'avatar')) { try { $user->employee->avatar = 'avatars/' . $filename; $user->employee->save(); } catch (\Exception $e) {} } } $user->save(); if ($user->employee) { try { $emp = $user->employee; $empFields = [ 'phone', 'date_of_birth', 'gender', 'address_line_1', 'address_line_2', 'city', 'state', 'country', 'postal_code', 'emergency_contact_name', 'emergency_contact_relationship', 'emergency_contact_number', 'bank_name', 'account_holder_name', 'account_number' ]; foreach ($empFields as $field) { if ($request->has($field)) { $emp->{$field} = $request->input($field); } } $emp->save(); } catch (\Exception $e) {} } $freshUser = $user->fresh('employee'); if (!empty($freshUser->avatar)) { $freshUser->avatar = get_file($freshUser->avatar); } if ($freshUser->employee && !empty($freshUser->employee->avatar)) { $freshUser->employee->avatar = get_file($freshUser->employee->avatar); } return response()->json([ 'success' => true, 'message' => 'Profile updated successfully', 'user' => $freshUser, 'data' => [ 'user' => $freshUser, 'employee' => $freshUser->employee, 'avatar' => $freshUser->avatar, ], 'errors' => null, ]); } }