user(); // Use a lock to prevent race conditions $lock = Cache::lock('instructor_application_' . $user->id, 10); if (!$lock->get()) { Session::flash('error', get_phrase('Another request is being processed. Please try again.')); return redirect()->back(); } try { // Check if application already exists if (Application::where('user_id', $user->id)->exists()) { Session::flash('error', get_phrase('Your request is in process. Please wait for admin to response.')); return redirect()->route('become.instructor'); } $rules = [ 'nidn' => ['required', 'string', 'max:11'], 'phone' => 'required', 'document' => ['required', 'file', 'mimes:pdf,doc,docx', 'max:2048'], 'description' => 'required', ]; // Validate data $validator = Validator::make($request->all(), $rules, [ 'nidn.required' => get_phrase('NIDN is required'), 'nidn.string' => get_phrase('NIDN must be a valid number'), 'nidn.max' => get_phrase('NIDN may not be greater than 11 characters'), 'phone.required' => get_phrase('Phone number is required'), 'document.required' => get_phrase('Document is required'), 'document.file' => get_phrase('Document must be a valid file'), 'document.mimes' => get_phrase('Document must be PDF, DOC, or DOCX'), 'document.max' => get_phrase('Document size must be less than 2MB'), 'description.required' => get_phrase('Description is required'), ]); if ($validator->fails()) { $firstError = $validator->errors()->first(); Session::flash('error', $firstError); return redirect()->back()->withErrors($validator)->withInput(); } DB::beginTransaction(); $result = $this->processInstructorApplication($request, $user); if ($result['success'] === false) { DB::rollBack(); Session::flash('error', $result['error']); return redirect()->back()->withInput(); } DB::commit(); Session::flash('success', get_phrase('Your application has been submitted successfully.')); return redirect()->route('home'); // Use named route instead of service provider } catch (\Exception $e) { DB::rollBack(); Log::error('Instructor application error:', [ 'error' => $e->getMessage(), 'trace' => $e->getTraceAsString(), 'user_id' => $user->id ]); Session::flash('error', get_phrase('Error during application submission. Please try again.')); return redirect()->back()->withInput(); } finally { $lock->release(); } } private function processInstructorApplication(Request $request, User $user) { $nidn = $request->nidn; // Check if NIDN already exists in Instructors table with status = 1 (active) $existingInstructor = Instructors::where('nidn', $nidn) ->where('status', 1) ->first(); if ($existingInstructor) { return ['success' => false, 'error' => get_phrase('This NIDN is already registered as an active instructor.')]; } // Check NIDN with the API $api_url = "https://sindig.unesa.ac.id/apipddikti/api?nidn={$nidn}&auto=1"; Log::info('Calling API for NIDN: ' . $nidn); try { $response = Http::timeout(30)->get($api_url); if (!$response->successful()) { Log::error('API request failed', [ 'status' => $response->status(), 'body' => $response->body() ]); return ['success' => false, 'error' => get_phrase('Unable to verify NIDN. Please try again later.')]; } $data = $response->json(); if (!isset($data['ok']) || $data['ok'] !== true || !isset($data['matched_dosen']) || !is_array($data['matched_dosen']) || count($data['matched_dosen']) == 0) { return ['success' => false, 'error' => get_phrase('NIDN not found in the system. Please check your NIDN.')]; } // Extract matched dosen data $matched_dosen = $data['matched_dosen'][0]; if (!isset($matched_dosen['nama'])) { Log::error('API response missing nama field', ['response' => $matched_dosen]); return ['success' => false, 'error' => get_phrase('Invalid API response. Please try again later.')]; } if (strtolower(trim($matched_dosen['nama'])) != strtolower(trim($user->name))) { Log::warning('Name mismatch', [ 'api_name' => $matched_dosen['nama'], 'user_name' => $user->name ]); return ['success' => false, 'error' => get_phrase('Name does not match PDDikti records. Please check your name.')]; } // Upload document $doc = $request->file('document'); $fileName = 'uploads/applications/' . $user->id . '_' . time() . '_' . Str::random(10) . '.' . $doc->getClientOriginalExtension(); $uploadResult = FileUploader::upload($doc, $fileName, null, null, 300); if (!$uploadResult) { return ['success' => false, 'error' => get_phrase('Document upload failed. Please try again.')]; } // Prepare instructor data $instructor = [ 'user_id' => $user->id, 'nidn' => $nidn, 'name' => $matched_dosen['nama'] ?? $user->name, 'id_sdm' => $matched_dosen['id'] ?? null, 'id_sms' => $matched_dosen['nama_prodi'] ?? null, 'id_pt' => $matched_dosen['nama_pt'] ?? null, 'status' => 0 ]; // Prepare application data $application = [ 'user_id' => $user->id, 'phone' => $request->phone, 'description' => $request->description, 'document' => $fileName, 'status' => 0 ]; // Create both application and instructor records $applicationRecord = Application::create($application); Instructors::create($instructor); // Send notification to user try { $user->notify(new \App\Notifications\InstructorApplicant($applicationRecord)); } catch (\Exception $e) { Log::error('Failed to send notification:', [ 'error' => $e->getMessage(), 'user_id' => $user->id, 'application_id' => $applicationRecord->id ]); } return ['success' => true, 'applicationRecord' => $applicationRecord]; } catch (\Exception $e) { Log::error('Process instructor application error:', [ 'error' => $e->getMessage(), 'trace' => $e->getTraceAsString(), 'user_id' => $user->id ]); return ['success' => false, 'error' => get_phrase('Error processing application. Please try again.')]; } } }