Initial commit of the complete City10 virtual city simulation platform. Backend (Laravel 13): - REST API with Sanctum tokens and session-based web authentication - Role-based access control (admin/user) with middleware protection - AI agent CRUD with OpenRouter.ai integration restricted to free models - Store management with 3D coordinates and AI shopkeeper assignment - Versioned legal documents (Terms, Privacy, Cookies) with LGPD audit trail - Math CAPTCHA on public forms and multi-language support (pt_BR, en, es) Frontend: - Responsive Blade templates with Tailwind CSS v4 - Three.js 3D city viewer with SSE chat streaming to AI agents - Admin panel for store and user administration Infrastructure: - MariaDB/MySQL database with comprehensive migrations - Database seeders for demo content and test accounts - PHPUnit test suite and Laravel Pint code styling - Vite build configuration with hot reload
124 lines
4.1 KiB
PHP
Executable File
124 lines
4.1 KiB
PHP
Executable File
<?php
|
|
|
|
namespace App\Http\Controllers\Api;
|
|
|
|
use App\Http\Controllers\Controller;
|
|
use App\Http\Requests\ConsentRequest;
|
|
use App\Models\LegalDocument;
|
|
use App\Models\UserConsent;
|
|
use Illuminate\Http\JsonResponse;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\DB;
|
|
|
|
/**
|
|
* Legal document and consent endpoints.
|
|
*
|
|
* Exposes the current Terms of Use, Privacy Policy and Cookie Policy and
|
|
* records auditable consent events required by the Brazilian LGPD.
|
|
*/
|
|
class LegalController extends Controller
|
|
{
|
|
/**
|
|
* Return the current version of every legal document.
|
|
*/
|
|
public function index(Request $request): JsonResponse
|
|
{
|
|
$locale = $request->query('locale', 'pt_BR');
|
|
|
|
$documents = LegalDocument::query()
|
|
->where('is_current', true)
|
|
->orderBy('type')
|
|
->get()
|
|
->map(fn (LegalDocument $doc): array => [
|
|
'id' => $doc->id,
|
|
'type' => $doc->type,
|
|
'version' => $doc->version,
|
|
'title' => $doc->title[$locale] ?? ($doc->title['pt_BR'] ?? $doc->type),
|
|
'body' => $doc->body[$locale] ?? ($doc->body['pt_BR'] ?? ''),
|
|
'published_at' => $doc->published_at?->toIso8601String(),
|
|
]);
|
|
|
|
return response()->json(['documents' => $documents]);
|
|
}
|
|
|
|
/**
|
|
* Return the consent status of the authenticated user.
|
|
*/
|
|
public function status(Request $request): JsonResponse
|
|
{
|
|
$user = $request->user();
|
|
|
|
$current = LegalDocument::query()->where('is_current', true)->get();
|
|
$accepted = $user->consents()->get(['legal_document_id', 'accepted_at']);
|
|
|
|
$items = $current->map(fn (LegalDocument $doc): array => [
|
|
'id' => $doc->id,
|
|
'type' => $doc->type,
|
|
'version' => $doc->version,
|
|
'accepted' => $accepted->contains('legal_document_id', $doc->id),
|
|
'accepted_at' => $accepted->firstWhere('legal_document_id', $doc->id)?->accepted_at?->toIso8601String(),
|
|
]);
|
|
|
|
return response()->json([
|
|
'all_accepted' => $items->every(fn (array $item): bool => $item['accepted']),
|
|
'documents' => $items,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Record the acceptance of all current legal documents.
|
|
*
|
|
* The event is stored with timestamp, client IP, user agent and the
|
|
* exact document versions accepted, forming an immutable audit trail.
|
|
*/
|
|
public function accept(ConsentRequest $request): JsonResponse
|
|
{
|
|
$user = $request->user();
|
|
|
|
$documents = LegalDocument::query()->where('is_current', true)->get();
|
|
|
|
DB::transaction(function () use ($user, $documents, $request): void {
|
|
foreach ($documents as $document) {
|
|
UserConsent::updateOrCreate(
|
|
[
|
|
'user_id' => $user->id,
|
|
'legal_document_id' => $document->id,
|
|
],
|
|
[
|
|
'accepted_at' => now(),
|
|
'ip_address' => $request->ip(),
|
|
'user_agent' => (string) $request->userAgent(),
|
|
'source' => 'web_form',
|
|
],
|
|
);
|
|
}
|
|
});
|
|
|
|
return response()->json([
|
|
'message' => __('legal.accepted'),
|
|
'accepted_at' => now()->toIso8601String(),
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Return the full consent audit history of the authenticated user.
|
|
*/
|
|
public function history(Request $request): JsonResponse
|
|
{
|
|
$history = $request->user()
|
|
->consents()
|
|
->with('document:id,type,version')
|
|
->orderByDesc('accepted_at')
|
|
->get()
|
|
->map(fn (UserConsent $consent): array => [
|
|
'document_type' => $consent->document?->type,
|
|
'document_version' => $consent->document?->version,
|
|
'accepted_at' => $consent->accepted_at?->toIso8601String(),
|
|
'ip_address' => $consent->ip_address,
|
|
'source' => $consent->source,
|
|
]);
|
|
|
|
return response()->json(['history' => $history]);
|
|
}
|
|
}
|