put('captcha_result', $a + $b); session()->put('captcha_expires_at', now()->addMinutes(10)->getTimestamp()); return response()->json([ 'question' => sprintf('%d + %d = ?', $a, $b), ]); } /** * Register a new regular user account. * * Applies a simple math CAPTCHA and immediately records acceptance of * the current legal documents when the client confirms them. */ public function register(RegisterRequest $request): JsonResponse { $data = $request->validated(); $user = User::create([ 'name' => $data['name'], 'email' => $data['email'], 'password' => $data['password'], 'role' => User::ROLE_USER, 'locale' => $data['locale'] ?? 'pt_BR', 'is_active' => true, ]); $token = $user->createToken('city10-api')->plainTextToken; return response()->json([ 'user' => $user, 'token' => $token, 'token_type' => 'Bearer', ], 201); } /** * Authenticate a user and return an API token. */ public function login(LoginRequest $request): JsonResponse { $user = User::where('email', $request->validated('email'))->first(); if ($user === null || ! Hash::check($request->validated('password'), $user->password)) { throw ValidationException::withMessages([ 'email' => [__('auth.failed')], ]); } if (! $user->is_active) { throw ValidationException::withMessages([ 'email' => [__('auth.disabled')], ]); } $user->forceFill(['last_login_at' => now()])->save(); $token = $user->createToken('city10-api')->plainTextToken; return response()->json([ 'user' => $user, 'token' => $token, 'token_type' => 'Bearer', 'requires_terms' => ! $user->hasAcceptedCurrentTerms(), ]); } /** * Update the authenticated user's own profile (name, locale, password). * * Role and active status are intentionally excluded: those are managed * exclusively through the admin endpoints. */ public function updateProfile(Request $request): JsonResponse { $data = $request->validate([ 'name' => ['sometimes', 'string', 'max:120'], 'locale' => ['sometimes', Rule::in(['pt_BR', 'en', 'es'])], 'password' => ['sometimes', 'string', 'min:8', 'confirmed'], ]); $user = $request->user(); if (! empty($data['password'])) { $user->password = $data['password']; unset($data['password']); } $user->fill($data)->save(); return response()->json($user); } /** * Revoke the token used for the current request. */ public function logout(Request $request): JsonResponse { $request->user()->currentAccessToken()?->delete(); return response()->json(['message' => __('auth.logged_out')]); } /** * Return the authenticated user plus consent status. */ public function me(Request $request): JsonResponse { $user = $request->user(); $currentDocuments = LegalDocument::query() ->where('is_current', true) ->get(['id', 'type', 'version']); $acceptedIds = $user->consents()->pluck('legal_document_id'); return response()->json([ 'user' => $user, 'requires_terms' => ! $user->hasAcceptedCurrentTerms(), 'pending_documents' => $currentDocuments ->reject(fn ($doc) => $acceptedIds->contains($doc->id)) ->values(), ]); } }