feat: add City10 platform with Laravel 13, AI agents, 3D viewer, and LGPD compliance
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
This commit is contained in:
Executable
+1
@@ -0,0 +1 @@
|
||||
*.sqlite*
|
||||
Executable
+68
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
* @extends Factory<User>
|
||||
*/
|
||||
class UserFactory extends Factory
|
||||
{
|
||||
/**
|
||||
* The current password being used by the factory.
|
||||
*/
|
||||
protected static ?string $password;
|
||||
|
||||
/**
|
||||
* Define the model's default state.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function definition(): array
|
||||
{
|
||||
return [
|
||||
'name' => fake()->name(),
|
||||
'email' => fake()->unique()->safeEmail(),
|
||||
'email_verified_at' => now(),
|
||||
'password' => static::$password ??= Hash::make('password'),
|
||||
'remember_token' => Str::random(10),
|
||||
'role' => 'user',
|
||||
'locale' => 'pt_BR',
|
||||
'is_active' => true,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that the model's email address should be unverified.
|
||||
*/
|
||||
public function unverified(): static
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'email_verified_at' => null,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that the account holds administrator privileges.
|
||||
*/
|
||||
public function admin(): static
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'role' => 'admin',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that the account has been deactivated.
|
||||
*/
|
||||
public function inactive(): static
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'is_active' => false,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('users', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('name');
|
||||
$table->string('email')->unique();
|
||||
$table->timestamp('email_verified_at')->nullable();
|
||||
$table->string('password');
|
||||
$table->rememberToken();
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
Schema::create('password_reset_tokens', function (Blueprint $table) {
|
||||
$table->string('email')->primary();
|
||||
$table->string('token');
|
||||
$table->timestamp('created_at')->nullable();
|
||||
});
|
||||
|
||||
Schema::create('sessions', function (Blueprint $table) {
|
||||
$table->string('id')->primary();
|
||||
$table->foreignId('user_id')->nullable()->index();
|
||||
$table->string('ip_address', 45)->nullable();
|
||||
$table->text('user_agent')->nullable();
|
||||
$table->longText('payload');
|
||||
$table->integer('last_activity')->index();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('users');
|
||||
Schema::dropIfExists('password_reset_tokens');
|
||||
Schema::dropIfExists('sessions');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('cache', function (Blueprint $table) {
|
||||
$table->string('key')->primary();
|
||||
$table->mediumText('value');
|
||||
$table->bigInteger('expiration')->index();
|
||||
});
|
||||
|
||||
Schema::create('cache_locks', function (Blueprint $table) {
|
||||
$table->string('key')->primary();
|
||||
$table->string('owner');
|
||||
$table->bigInteger('expiration')->index();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('cache');
|
||||
Schema::dropIfExists('cache_locks');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('jobs', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('queue')->index();
|
||||
$table->longText('payload');
|
||||
$table->unsignedSmallInteger('attempts');
|
||||
$table->unsignedInteger('reserved_at')->nullable();
|
||||
$table->unsignedInteger('available_at');
|
||||
$table->unsignedInteger('created_at');
|
||||
});
|
||||
|
||||
Schema::create('job_batches', function (Blueprint $table) {
|
||||
$table->string('id')->primary();
|
||||
$table->string('name');
|
||||
$table->integer('total_jobs');
|
||||
$table->integer('pending_jobs');
|
||||
$table->integer('failed_jobs');
|
||||
$table->longText('failed_job_ids');
|
||||
$table->mediumText('options')->nullable();
|
||||
$table->integer('cancelled_at')->nullable();
|
||||
$table->integer('created_at');
|
||||
$table->integer('finished_at')->nullable();
|
||||
});
|
||||
|
||||
Schema::create('failed_jobs', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('uuid')->unique();
|
||||
$table->string('connection');
|
||||
$table->string('queue');
|
||||
$table->longText('payload');
|
||||
$table->longText('exception');
|
||||
$table->timestamp('failed_at')->useCurrent();
|
||||
|
||||
$table->index(['connection', 'queue', 'failed_at']);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('jobs');
|
||||
Schema::dropIfExists('job_batches');
|
||||
Schema::dropIfExists('failed_jobs');
|
||||
}
|
||||
};
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('personal_access_tokens', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->morphs('tokenable');
|
||||
$table->text('name');
|
||||
$table->string('token', 64)->unique();
|
||||
$table->text('abilities')->nullable();
|
||||
$table->timestamp('last_used_at')->nullable();
|
||||
$table->timestamp('expires_at')->nullable()->index();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('personal_access_tokens');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
/**
|
||||
* Adds role-based access control columns to the users table.
|
||||
*
|
||||
* Distinguishes administrators ("admin") from regular users ("user")
|
||||
* so that API endpoints and the admin panel can be protected per role.
|
||||
*/
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table): void {
|
||||
// Role used for authorization: "admin" or "user".
|
||||
$table->string('role', 20)->default('user')->after('password')->index();
|
||||
|
||||
// Preferred UI language: pt_BR, en, or es.
|
||||
$table->string('locale', 5)->default('pt_BR')->after('role');
|
||||
|
||||
// Soft-delete flag so accounts can be disabled without data loss.
|
||||
$table->boolean('is_active')->default(true)->after('locale');
|
||||
|
||||
// Timestamp of the latest successful authentication (audit aid).
|
||||
$table->timestamp('last_login_at')->nullable()->after('is_active');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table): void {
|
||||
$table->dropColumn(['role', 'locale', 'is_active', 'last_login_at']);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
/**
|
||||
* Creates the "ai_agents" table.
|
||||
*
|
||||
* Each AI agent represents an autonomous NPC or manager entity inside the
|
||||
* virtual city. Decisions are delegated to models served by OpenRouter.ai,
|
||||
* restricted to an allow-list of free models configured by the administrator.
|
||||
*/
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('ai_agents', function (Blueprint $table): void {
|
||||
$table->id();
|
||||
|
||||
// Human readable label, e.g. "Prefeito", "Lojista Padaria Central".
|
||||
$table->string('name', 120);
|
||||
|
||||
// Agent archetype: mayor, shopkeeper, pedestrian, traffic_officer, etc.
|
||||
$table->string('type', 40)->index();
|
||||
|
||||
// Free OpenRouter model identifier used for reasoning.
|
||||
$table->string('model', 120)->default('deepseek/deepseek-v4-flash-0731');
|
||||
|
||||
// System prompt describing the agent persona and constraints.
|
||||
$table->text('system_prompt')->nullable();
|
||||
|
||||
// Current macro goal in natural language (persisted AI state).
|
||||
$table->text('current_goal')->nullable();
|
||||
|
||||
// JSON snapshot of the latest decision payload returned by the model.
|
||||
$table->json('last_decision')->nullable();
|
||||
|
||||
// Operational state: active, paused, thinking, error.
|
||||
$table->string('status', 20)->default('active')->index();
|
||||
|
||||
// Number of consecutive failures (used for backoff/circuit breaker).
|
||||
$table->unsignedInteger('error_count')->default(0);
|
||||
|
||||
// 3D anchor position of the agent inside the city scene.
|
||||
$table->decimal('pos_x', 12, 4)->default(0);
|
||||
$table->decimal('pos_y', 12, 4)->default(0);
|
||||
$table->decimal('pos_z', 12, 4)->default(0);
|
||||
|
||||
// Rotation on the Y axis (radians) for 3D rendering.
|
||||
$table->decimal('rotation_y', 10, 6)->default(0);
|
||||
|
||||
$table->timestamp('last_think_at')->nullable();
|
||||
$table->timestamps();
|
||||
|
||||
$table->index(['type', 'status']);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('ai_agents');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
/**
|
||||
* Creates the "stores" table.
|
||||
*
|
||||
* Stores are physical shop buildings placed in the 3D city at an exact
|
||||
* (x, y, z) coordinate and are operated by an optional AI shopkeeper agent.
|
||||
*/
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('stores', function (Blueprint $table): void {
|
||||
$table->id();
|
||||
|
||||
// Store display name, e.g. "Padaria Central".
|
||||
$table->string('name', 160);
|
||||
|
||||
// Free text description shown in the city UI.
|
||||
$table->text('description')->nullable();
|
||||
|
||||
// Store category: bakery, market, hardware, restaurant, etc.
|
||||
$table->string('type', 60)->index();
|
||||
|
||||
// Owner account (regular user acting as shopkeeper).
|
||||
$table->foreignId('owner_id')->nullable()->constrained('users')->nullOnDelete();
|
||||
|
||||
// AI shopkeeper agent driving prices and decisions (optional).
|
||||
$table->foreignId('agent_id')->nullable()->constrained('ai_agents')->nullOnDelete();
|
||||
|
||||
// 3D placement inside the virtual city scene.
|
||||
$table->decimal('pos_x', 12, 4)->default(0);
|
||||
$table->decimal('pos_y', 12, 4)->default(0);
|
||||
$table->decimal('pos_z', 12, 4)->default(0);
|
||||
|
||||
// Building footprint dimensions in world units (used by the renderer).
|
||||
$table->decimal('width', 10, 4)->default(4);
|
||||
$table->decimal('height', 10, 4)->default(4);
|
||||
$table->decimal('depth', 10, 4)->default(4);
|
||||
|
||||
// Commercial state of the store.
|
||||
$table->boolean('is_open')->default(true)->index();
|
||||
|
||||
// Current revenue in virtual currency (updated by AI/players).
|
||||
$table->decimal('revenue', 14, 2)->default(0);
|
||||
|
||||
$table->timestamps();
|
||||
|
||||
$table->index(['pos_x', 'pos_y', 'pos_z']);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('stores');
|
||||
}
|
||||
};
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
/**
|
||||
* Creates legal document and consent tables.
|
||||
*
|
||||
* Stores versioned legal documents (Terms of Use, Privacy Policy and Cookie
|
||||
* Policy, all aligned with the Brazilian LGPD) together with an immutable
|
||||
* audit trail of each user consent event.
|
||||
*/
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('legal_documents', function (Blueprint $table): void {
|
||||
$table->id();
|
||||
|
||||
// Document kind: terms, privacy, cookies.
|
||||
$table->string('type', 20)->index();
|
||||
|
||||
// Version identifier, e.g. "1.0.0" (incremented on edits).
|
||||
$table->string('version', 20);
|
||||
|
||||
// Title localized per language key stored in JSON: {"pt_BR":..,"en":..,"es":..}
|
||||
$table->json('title');
|
||||
|
||||
// Full document body per language (markdown/HTML-safe text).
|
||||
$table->json('body');
|
||||
|
||||
// Whether this version is the one new users must accept.
|
||||
$table->boolean('is_current')->default(true)->index();
|
||||
|
||||
$table->timestamp('published_at')->nullable();
|
||||
$table->timestamps();
|
||||
|
||||
// A given type+version pair can only exist once.
|
||||
$table->unique(['type', 'version']);
|
||||
});
|
||||
|
||||
Schema::create('user_consents', function (Blueprint $table): void {
|
||||
$table->id();
|
||||
|
||||
$table->foreignId('user_id')->constrained('users')->cascadeOnDelete();
|
||||
$table->foreignId('legal_document_id')->constrained('legal_documents')->cascadeOnDelete();
|
||||
|
||||
// Moment the user accepted this specific document version.
|
||||
$table->timestamp('accepted_at');
|
||||
|
||||
// Client IP and user agent captured at acceptance (LGPD audit).
|
||||
$table->string('ip_address', 45)->nullable();
|
||||
$table->text('user_agent')->nullable();
|
||||
|
||||
// Consent channel, e.g. "web_form", "api".
|
||||
$table->string('source', 30)->default('web_form');
|
||||
|
||||
$table->timestamps();
|
||||
|
||||
// One consent per user per document version (idempotent audit).
|
||||
$table->unique(['user_id', 'legal_document_id']);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('user_consents');
|
||||
Schema::dropIfExists('legal_documents');
|
||||
}
|
||||
};
|
||||
Executable
+128
@@ -0,0 +1,128 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use App\Models\AiAgent;
|
||||
use App\Models\Store;
|
||||
use App\Models\User;
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
/**
|
||||
* Seeds a starter set of AI agents and stores for the virtual city.
|
||||
*
|
||||
* Provides the Mayor, a few shopkeepers and pedestrian agents together
|
||||
* with representative buildings so that the 3D scene has content to
|
||||
* render on first load.
|
||||
*/
|
||||
class CityContentSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Seed AI agents and stores.
|
||||
*/
|
||||
public function run(): void
|
||||
{
|
||||
$shopkeeper = User::where('email', 'user@sistema.net')->first();
|
||||
|
||||
// --- Mayor: coordinates the city at a macro level -----------------
|
||||
$mayor = AiAgent::updateOrCreate(
|
||||
['name' => 'Prefeito Augusto'],
|
||||
[
|
||||
'type' => 'mayor',
|
||||
'model' => config('ai.default_model'),
|
||||
'system_prompt' => 'Você é o Prefeito da cidade virtual City10. Tome decisões macro sobre infraestrutura, tráfego e qualidade de vida dos cidadãos.',
|
||||
'current_goal' => 'Manter a cidade segura e próspera',
|
||||
'status' => 'active',
|
||||
'pos_x' => 0, 'pos_y' => 0, 'pos_z' => 0,
|
||||
],
|
||||
);
|
||||
|
||||
// --- Shopkeepers --------------------------------------------------
|
||||
$baker = AiAgent::updateOrCreate(
|
||||
['name' => 'Seu João — Padaria Central'],
|
||||
[
|
||||
'type' => 'shopkeeper',
|
||||
'model' => config('ai.default_model'),
|
||||
'system_prompt' => 'Você é o dono da Padaria Central. Gerencie estoque, preços e atendimento com simpatia.',
|
||||
'current_goal' => 'Aumentar as vendas da manhã',
|
||||
'status' => 'active',
|
||||
'pos_x' => 12, 'pos_y' => 0, 'pos_z' => -8,
|
||||
],
|
||||
);
|
||||
|
||||
// --- Pedestrians --------------------------------------------------
|
||||
AiAgent::updateOrCreate(
|
||||
['name' => 'Nara'],
|
||||
[
|
||||
'type' => 'pedestrian',
|
||||
'model' => config('ai.default_model'),
|
||||
'system_prompt' => 'Você é a Nara, uma pedestre amigável. Converse brevemente com quem se aproximar.',
|
||||
'current_goal' => 'Caminhar até a praça',
|
||||
'status' => 'active',
|
||||
'pos_x' => 20, 'pos_y' => 0, 'pos_z' => 10,
|
||||
],
|
||||
);
|
||||
|
||||
AiAgent::updateOrCreate(
|
||||
['name' => 'Wilson'],
|
||||
[
|
||||
'type' => 'pedestrian',
|
||||
'model' => config('ai.default_model'),
|
||||
'system_prompt' => 'Você é o Wilson, um pedestre que trabalha na loja de material de construção.',
|
||||
'current_goal' => 'Voltar para casa',
|
||||
'status' => 'active',
|
||||
'pos_x' => 6, 'pos_y' => 0, 'pos_z' => 4,
|
||||
],
|
||||
);
|
||||
|
||||
AiAgent::updateOrCreate(
|
||||
['name' => 'Amanda'],
|
||||
[
|
||||
'type' => 'pedestrian',
|
||||
'model' => config('ai.default_model'),
|
||||
'system_prompt' => 'Você é a Amanda, criadora de conteúdo. Gosta de conversar sobre arte e moda.',
|
||||
'current_goal' => 'Fotografe a praça para o feed',
|
||||
'status' => 'active',
|
||||
'pos_x' => 30, 'pos_y' => 0, 'pos_z' => 6,
|
||||
],
|
||||
);
|
||||
|
||||
// --- Stores -------------------------------------------------------
|
||||
Store::updateOrCreate(
|
||||
['name' => 'Padaria Central'],
|
||||
[
|
||||
'description' => 'Pães frescos e cafés desde 1998.',
|
||||
'type' => 'bakery',
|
||||
'owner_id' => $shopkeeper?->id,
|
||||
'agent_id' => $baker->id,
|
||||
'pos_x' => 12, 'pos_y' => 0, 'pos_z' => -12,
|
||||
'width' => 8, 'height' => 6, 'depth' => 8,
|
||||
'is_open' => true,
|
||||
],
|
||||
);
|
||||
|
||||
Store::updateOrCreate(
|
||||
['name' => 'Mercado Municipal'],
|
||||
[
|
||||
'description' => 'Produtos frescos e itens do dia a dia.',
|
||||
'type' => 'market',
|
||||
'owner_id' => $shopkeeper?->id,
|
||||
'agent_id' => $mayor->id,
|
||||
'pos_x' => -18, 'pos_y' => 0, 'pos_z' => 14,
|
||||
'width' => 14, 'height' => 8, 'depth' => 12,
|
||||
'is_open' => true,
|
||||
],
|
||||
);
|
||||
|
||||
Store::updateOrCreate(
|
||||
['name' => 'Loja de Materiais da Esquina'],
|
||||
[
|
||||
'description' => 'Material de construção e ferramentas.',
|
||||
'type' => 'hardware',
|
||||
'owner_id' => $shopkeeper?->id,
|
||||
'pos_x' => 8, 'pos_y' => 0, 'pos_z' => 6,
|
||||
'width' => 10, 'height' => 7, 'depth' => 10,
|
||||
'is_open' => true,
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
Executable
+29
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
/**
|
||||
* Master seeder orchestrating all initial dataset loaders.
|
||||
*
|
||||
* Order matters: legal documents and users are created first so that
|
||||
* foreign keys from agents and stores resolve correctly.
|
||||
*/
|
||||
class DatabaseSeeder extends Seeder
|
||||
{
|
||||
use WithoutModelEvents;
|
||||
|
||||
/**
|
||||
* Seed the application's database.
|
||||
*/
|
||||
public function run(): void
|
||||
{
|
||||
$this->call([
|
||||
LegalDocumentSeeder::class,
|
||||
UserSeeder::class,
|
||||
CityContentSeeder::class,
|
||||
]);
|
||||
}
|
||||
}
|
||||
Executable
+442
@@ -0,0 +1,442 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use App\Models\LegalDocument;
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
/**
|
||||
* Seeds the initial version (1.0.0) of the three legal documents.
|
||||
*
|
||||
* Content is provided in Portuguese (Brazil), English and Spanish and
|
||||
* covers the requirements of the Brazilian LGPD (Lei nº 13.709/2018):
|
||||
* data collection, storage, processing, sharing, deletion rights and the
|
||||
* cookie policy with consent management.
|
||||
*/
|
||||
class LegalDocumentSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Seed the legal documents table.
|
||||
*/
|
||||
public function run(): void
|
||||
{
|
||||
$publishedAt = now();
|
||||
|
||||
LegalDocument::updateOrCreate(
|
||||
['type' => LegalDocument::TYPE_TERMS, 'version' => '1.0.0'],
|
||||
[
|
||||
'title' => [
|
||||
'pt_BR' => 'Termos de Uso e Política de Privacidade',
|
||||
'en' => 'Terms of Use and Privacy Policy',
|
||||
'es' => 'Términos de Uso y Política de Privacidad',
|
||||
],
|
||||
'body' => [
|
||||
'pt_BR' => $this->termsPtBr(),
|
||||
'en' => $this->termsEn(),
|
||||
'es' => $this->termsEs(),
|
||||
],
|
||||
'is_current' => true,
|
||||
'published_at' => $publishedAt,
|
||||
],
|
||||
);
|
||||
|
||||
LegalDocument::updateOrCreate(
|
||||
['type' => LegalDocument::TYPE_PRIVACY, 'version' => '1.0.0'],
|
||||
[
|
||||
'title' => [
|
||||
'pt_BR' => 'Política de Privacidade (LGPD)',
|
||||
'en' => 'Privacy Policy (LGPD)',
|
||||
'es' => 'Política de Privacidad (LGPD)',
|
||||
],
|
||||
'body' => [
|
||||
'pt_BR' => $this->privacyPtBr(),
|
||||
'en' => $this->privacyEn(),
|
||||
'es' => $this->privacyEs(),
|
||||
],
|
||||
'is_current' => true,
|
||||
'published_at' => $publishedAt,
|
||||
],
|
||||
);
|
||||
|
||||
LegalDocument::updateOrCreate(
|
||||
['type' => LegalDocument::TYPE_COOKIES, 'version' => '1.0.0'],
|
||||
[
|
||||
'title' => [
|
||||
'pt_BR' => 'Política de Cookies',
|
||||
'en' => 'Cookie Policy',
|
||||
'es' => 'Política de Cookies',
|
||||
],
|
||||
'body' => [
|
||||
'pt_BR' => $this->cookiesPtBr(),
|
||||
'en' => $this->cookiesEn(),
|
||||
'es' => $this->cookiesEs(),
|
||||
],
|
||||
'is_current' => true,
|
||||
'published_at' => $publishedAt,
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Terms of Use body in Portuguese (Brazil).
|
||||
*/
|
||||
private function termsPtBr(): string
|
||||
{
|
||||
return <<<'TXT'
|
||||
# Termos de Uso — City10
|
||||
|
||||
**Versão 1.0.0** — Vigência a partir da data de aceite pelo usuário.
|
||||
|
||||
## 1. Objeto
|
||||
O City10 é uma plataforma de simulação urbana 3D com agentes de inteligência artificial, operada pela Syspanel (contato@syspanel.com.br). Ao criar uma conta e utilizar a plataforma, o usuário declara ter lido e concordado com estes Termos e com a Política de Privacidade.
|
||||
|
||||
## 2. Cadastro e Conta
|
||||
2.1. O usuário deve fornecer informações verdadeiras (nome e e-mail).
|
||||
2.2. A conta é pessoal e intransferível. O usuário é responsável pela guarda de sua senha.
|
||||
2.3. Contas administrativas possuem privilégios adicionais de gestão da cidade e dos agentes de IA.
|
||||
|
||||
## 3. Uso Aceitável
|
||||
3.1. É vedado utilizar a plataforma para fins ilícitos, ofensivos ou que violem direitos de terceiros.
|
||||
3.2. É vedado tentar contornar mecanismos de segurança, exceder limites de uso da API ou inserir conteúdo malicioso.
|
||||
3.3. Os agentes de IA operam com modelos de linguagem de terceiros (OpenRouter.ai). As decisões simuladas por esses agentes são ficção e não constituem aconselhamento profissional.
|
||||
|
||||
## 4. Propriedade Intelectual
|
||||
4.1. O software, marca City10 e respectivos código-fonte são propriedade da Syspanel, regidos pela Licença Proprietária anexa (LICENSE).
|
||||
4.2. O conteúdo gerado pelo usuário permanece de sua titulariedade, concedendo à plataforma a licença mínima necessária para exibição.
|
||||
|
||||
## 5. Limitação de Responsabilidade
|
||||
5.1. A plataforma é fornecida "no estado em que se encontra". A Syspanel não garante disponibilidade ininterrupta.
|
||||
5.2. A Syspanel não se responsabiliza por decisões tomadas com base em simulações produzidas pelos agentes de IA.
|
||||
|
||||
## 6. Encerramento
|
||||
6.1. A Syspanel pode suspender ou encerrar contas que violem estes Termos, mediante notificação quando possível.
|
||||
6.2. O usuário pode solicitar a exclusão de seus dados a qualquer momento (direito de eliminação, art. 18, VI, LGPD).
|
||||
|
||||
## 7. Foro
|
||||
Fica eleito o foro da comarca de Santa Rita do Sapucaí — MG, Brasil, para dirimir controvérsias decorrentes destes Termos.
|
||||
TXT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Terms of Use body in English.
|
||||
*/
|
||||
private function termsEn(): string
|
||||
{
|
||||
return <<<'TXT'
|
||||
# Terms of Use — City10
|
||||
|
||||
**Version 1.0.0** — Effective from the date of user acceptance.
|
||||
|
||||
## 1. Purpose
|
||||
City10 is a 3D urban simulation platform with artificial intelligence agents, operated by Syspanel (contato@syspanel.com.br). By creating an account and using the platform, the user acknowledges having read and agreed to these Terms and to the Privacy Policy.
|
||||
|
||||
## 2. Registration and Account
|
||||
2.1. Users must provide truthful information (name and email).
|
||||
2.2. Accounts are personal and non-transferable. The user is responsible for safeguarding their password.
|
||||
2.3. Administrative accounts hold additional privileges to manage the city and its AI agents.
|
||||
|
||||
## 3. Acceptable Use
|
||||
3.1. Using the platform for unlawful, offensive purposes or that infringe third-party rights is prohibited.
|
||||
3.2. Circumventing security mechanisms, exceeding API usage limits or injecting malicious content is prohibited.
|
||||
3.3. AI agents run on third-party language models (OpenRouter.ai). Agent decisions are fictional and do not constitute professional advice.
|
||||
|
||||
## 4. Intellectual Property
|
||||
4.1. The software, the City10 brand and its source code are property of Syspanel, governed by the proprietary License attached (LICENSE).
|
||||
4.2. User-generated content remains owned by the user, who grants the platform the minimal licence required for display.
|
||||
|
||||
## 5. Limitation of Liability
|
||||
5.1. The platform is provided "as is". Syspanel does not guarantee uninterrupted availability.
|
||||
5.2. Syspanel is not liable for decisions taken based on simulations produced by AI agents.
|
||||
|
||||
## 6. Termination
|
||||
6.1. Syspanel may suspend or terminate accounts that violate these Terms, with notice when possible.
|
||||
6.2. Users may request deletion of their data at any time (right of erasure, art. 18, VI, LGPD).
|
||||
|
||||
## 7. Governing Law
|
||||
The courts of Santa Rita do Sapucaí — MG, Brazil, are elected to settle disputes arising from these Terms.
|
||||
TXT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Terms of Use body in Spanish.
|
||||
*/
|
||||
private function termsEs(): string
|
||||
{
|
||||
return <<<'TXT'
|
||||
# Términos de Uso — City10
|
||||
|
||||
**Versión 1.0.0** — Vigente desde la fecha de aceptación por el usuario.
|
||||
|
||||
## 1. Objeto
|
||||
City10 es una plataforma de simulación urbana 3D con agentes de inteligencia artificial, operada por Syspanel (contato@syspanel.com.br). Al crear una cuenta y utilizar la plataforma, el usuario declara haber leído y aceptado estos Términos y la Política de Privacidad.
|
||||
|
||||
## 2. Registro y Cuenta
|
||||
2.1. El usuario debe proporcionar información verídica (nombre y correo electrónico).
|
||||
2.2. La cuenta es personal e intransferible. El usuario es responsable de la custodia de su contraseña.
|
||||
2.3. Las cuentas administrativas poseen privilegios adicionales de gestión de la ciudad y de los agentes de IA.
|
||||
|
||||
## 3. Uso Aceptable
|
||||
3.1. Está prohibido utilizar la plataforma con fines ilícitos, ofensivos o que violen derechos de terceros.
|
||||
3.2. Está prohibido eludir mecanismos de seguridad, exceder límites de uso de la API o inyectar contenido malicioso.
|
||||
3.3. Los agentes de IA operan con modelos de lenguaje de terceros (OpenRouter.ai). Las decisiones simuladas son ficción y no constituyen asesoramiento profesional.
|
||||
|
||||
## 4. Propiedad Intelectual
|
||||
4.1. El software, la marca City10 y su código fuente son propiedad de Syspanel, regidos por la Licencia Proprietaria adjunta (LICENSE).
|
||||
4.2. El contenido generado por el usuario sigue siendo de su titularidad, concediendo a la plataforma la licencia mínima necesaria para su exhibición.
|
||||
|
||||
## 5. Limitación de Responsabilidad
|
||||
5.1. La plataforma se ofrece "tal cual". Syspanel no garantiza disponibilidad ininterrumpida.
|
||||
5.2. Syspanel no se responsabiliza por decisiones tomadas en base a simulaciones producidas por los agentes de IA.
|
||||
|
||||
## 6. Terminación
|
||||
6.1. Syspanel puede suspender o terminar cuentas que violen estos Términos, con notificación cuando sea posible.
|
||||
6.2. El usuario puede solicitar la eliminación de sus datos en cualquier momento (derecho de supresión, art. 18, VI, LGPD).
|
||||
|
||||
## 7. Ley Aplicable
|
||||
Se elige el foro de Santa Rita do Sapucaí — MG, Brasil, para resolver controversias derivadas de estos Términos.
|
||||
TXT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Privacy Policy body in Portuguese (Brazil).
|
||||
*/
|
||||
private function privacyPtBr(): string
|
||||
{
|
||||
return <<<'TXT'
|
||||
# Política de Privacidade — City10 (LGPD)
|
||||
|
||||
**Versão 1.0.0** — Em conformidade com a Lei nº 13.709/2018 (LGPD).
|
||||
|
||||
## 1. Controlador dos Dados
|
||||
Syspanel — contato@syspanel.com.br — Santa Rita do Sapucaí/MG, Brasil.
|
||||
|
||||
## 2. Dados Pessoais Coletados
|
||||
2.1. **Dados de cadastro:** nome, e-mail e senha (armazenada em formato irreversível — hash bcrypt).
|
||||
2.2. **Dados de consentimento:** data/hora, versão dos documentos aceitos, endereço IP e user agent (auditoria LGPD).
|
||||
2.3. **Dados de uso:** registros de acesso (logs) e preferência de idioma.
|
||||
|
||||
## 3. Finalidades e Bases Legais
|
||||
3.1. **Execução do contrato** (art. 7º, V): prestação da plataforma, autenticação e funcionamento da cidade 3D.
|
||||
3.2. **Consentimento** (art. 7º, I): aceite dos Termos, Política de Privacidade e Política de Cookies.
|
||||
3.3. **Legítimo interesse** (art. 7º, IX): segurança, prevenção a fraudes e melhoria do serviço, com testes de balanceamento.
|
||||
|
||||
## 4. Compartilhamento
|
||||
4.1. Os dados são compartilhados exclusivamente com: (i) provedor de infraestrutura ( hospedagem em Docker ); (ii) OpenRouter.ai, exclusivamente com prompts contendo contexto simulado da cidade — nunca dados cadastrais do usuário.
|
||||
4.2. Não vendemos dados pessoais a terceiros.
|
||||
|
||||
## 5. Transferência Internacional
|
||||
O serviço de modelos de IA (OpenRouter.ai) pode processar prompts fora do Brasil. Nesses casos, adotamos salvaguardas contratuais adequadas.
|
||||
|
||||
## 6. Tempo de Conservação
|
||||
6.1. Dados de cadastro: enquanto a conta estiver ativa + 6 meses após encerramento.
|
||||
6.2. Registros de consentimento: pelo prazo mínimo de 5 anos (art. 37, LGPD — dever de guarda de registros).
|
||||
|
||||
## 7. Direitos do Titular (art. 18)
|
||||
O usuário pode solicitar: confirmação de tratamento; acesso; correção; anonimização, bloqueio ou eliminação; portabilidade; informação sobre compartilhamento; revogação do consentimento. Solicitações: contato@syspanel.com.br, respondidas em até 15 dias.
|
||||
|
||||
## 8. Segurança
|
||||
Utilizamos hash de senhas (bcrypt), controle de acesso por papel (roles), tokens de API revogáveis e trilhas de auditoria.
|
||||
|
||||
## 9. Encarregado de Dados (DPO)
|
||||
contato@syspanel.com.br — canal direto para o Encarregado pelo tratamento de dados pessoais.
|
||||
TXT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Privacy Policy body in English.
|
||||
*/
|
||||
private function privacyEn(): string
|
||||
{
|
||||
return <<<'TXT'
|
||||
# Privacy Policy — City10 (LGPD)
|
||||
|
||||
**Version 1.0.0** — Compliant with Brazilian Law nº 13.709/2018 (LGPD).
|
||||
|
||||
## 1. Data Controller
|
||||
Syspanel — contato@syspanel.com.br — Santa Rita do Sapucaí/MG, Brazil.
|
||||
|
||||
## 2. Personal Data Collected
|
||||
2.1. **Registration data:** name, email and password (stored irreversibly as a bcrypt hash).
|
||||
2.2. **Consent data:** date/time, accepted document versions, IP address and user agent (LGPD audit trail).
|
||||
2.3. **Usage data:** access logs and language preference.
|
||||
|
||||
## 3. Purposes and Legal Bases
|
||||
3.1. **Contract performance** (art. 7, V): platform delivery, authentication and 3D city operation.
|
||||
3.2. **Consent** (art. 7, I): acceptance of Terms, Privacy Policy and Cookie Policy.
|
||||
3.3. **Legitimate interest** (art. 7, IX): security, fraud prevention and service improvement.
|
||||
|
||||
## 4. Sharing
|
||||
4.1. Data is shared exclusively with: (i) the infrastructure provider (Docker hosting); (ii) OpenRouter.ai, strictly with prompts containing simulated city context — never user registration data.
|
||||
4.2. We do not sell personal data to third parties.
|
||||
|
||||
## 5. International Transfer
|
||||
The AI model service (OpenRouter.ai) may process prompts outside Brazil. Adequate contractual safeguards are applied.
|
||||
|
||||
## 6. Retention Period
|
||||
6.1. Registration data: while the account is active + 6 months after termination.
|
||||
6.2. Consent records: minimum of 5 years (art. 37, LGPD — record-keeping duty).
|
||||
|
||||
## 7. Data Subject Rights (art. 18)
|
||||
Users may request: confirmation of processing; access; correction; anonymisation, blocking or deletion; portability; information on sharing; withdrawal of consent. Requests: contato@syspanel.com.br, answered within 15 days.
|
||||
|
||||
## 8. Security
|
||||
We use password hashing (bcrypt), role-based access control, revocable API tokens and audit trails.
|
||||
|
||||
## 9. Data Protection Officer (DPO)
|
||||
contato@syspanel.com.br — direct channel to the officer responsible for personal data processing.
|
||||
TXT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Privacy Policy body in Spanish.
|
||||
*/
|
||||
private function privacyEs(): string
|
||||
{
|
||||
return <<<'TXT'
|
||||
# Política de Privacidad — City10 (LGPD)
|
||||
|
||||
**Versión 1.0.0** — En conformidad con la Ley nº 13.709/2018 (LGPD).
|
||||
|
||||
## 1. Controlador de los Datos
|
||||
Syspanel — contato@syspanel.com.br — Santa Rita do Sapucaí/MG, Brasil.
|
||||
|
||||
## 2. Datos Personales Recabados
|
||||
2.1. **Datos de registro:** nombre, correo electrónico y contraseña (almmacenada de forma irreversible — hash bcrypt).
|
||||
2.2. **Datos de consentimiento:** fecha/hora, versión de los documentos aceptados, dirección IP y user agent (auditoría LGPD).
|
||||
2.3. **Datos de uso:** registros de acceso (logs) e idioma preferido.
|
||||
|
||||
## 3. Finalidades y Bases Legales
|
||||
3.1. **Ejecución del contrato** (art. 7, V): prestación de la plataforma, autenticación y funcionamiento de la ciudad 3D.
|
||||
3.2. **Consentimiento** (art. 7, I): aceptación de los Términos, Política de Privacidad y Política de Cookies.
|
||||
3.3. **Interés legítimo** (art. 7, IX): seguridad, prevención de fraudes y mejora del servicio.
|
||||
|
||||
## 4. Compartición
|
||||
4.1. Los datos se comparten exclusivamente con: (i) el proveedor de infraestructura (hospedaje en Docker); (ii) OpenRouter.ai, estrictamente con prompts que contienen contexto simulado de la ciudad — nunca datos del usuario.
|
||||
4.2. No vendemos datos personales a terceros.
|
||||
|
||||
## 5. Transferencia Internacional
|
||||
El servicio de modelos de IA (OpenRouter.ai) puede procesar prompts fuera de Brasil. Se aplican salvaguardas contractuales adecuadas.
|
||||
|
||||
## 6. Plazo de Conservación
|
||||
6.1. Datos de registro: mientras la cuenta esté activa + 6 meses tras la finalización.
|
||||
6.2. Registros de consentimiento: plazo mínimo de 5 años (art. 37, LGPD).
|
||||
|
||||
## 7. Derechos del Titular (art. 18)
|
||||
El usuario puede solicitar: confirmación del tratamiento; acceso; corrección; anonimización, bloqueo o eliminación; portabilidad; información sobre compartición; revocación del consentimiento. Solicitudes: contato@syspanel.com.br, respondidas en hasta 15 días.
|
||||
|
||||
## 8. Seguridad
|
||||
Utilizamos hash de contraseñas (bcrypt), control de acceso por rol, tokens de API revocables y rastros de auditoría.
|
||||
|
||||
## 9. Encargado de Datos (DPO)
|
||||
contato@syspanel.com.br — canal directo con el encargado del tratamiento de datos personales.
|
||||
TXT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cookie Policy body in Portuguese (Brazil).
|
||||
*/
|
||||
private function cookiesPtBr(): string
|
||||
{
|
||||
return <<<'TXT'
|
||||
# Política de Cookies — City10
|
||||
|
||||
**Versão 1.0.0**
|
||||
|
||||
## 1. O que são cookies
|
||||
Cookies são pequenos arquivos de texto armazenados no navegador durante a navegação, essenciais para o funcionamento de sessões e preferências.
|
||||
|
||||
## 2. Cookies Utilizados
|
||||
|
||||
| Cookie | Tipo | Finalidade | Duração | Consentimento |
|
||||
|---|---|---|---|---|
|
||||
| city10-session | Estritamente necessário | Manter a sessão autenticada (CSRF + login) | Sessão / 120 min | Isento (necessário) |
|
||||
| XSRF-TOKEN | Estritamente necessário | Proteção contra ataques CSRF | Sessão | Isento (necessário) |
|
||||
| city10-locale | Desempenho | Guardar o idioma escolhido pelo usuário | 1 ano | Sim |
|
||||
| city10-consent | Estritamente necessário | Registrar a versão dos termos aceitos | 1 ano | Isento (necessário) |
|
||||
| city10-analytics | Analítico | Métricas agregadas de uso (futuro) | 90 dias | Sim |
|
||||
|
||||
## 3. Gestão do Consentimento
|
||||
3.1. Ao acessar a plataforma, o usuário visualiza o banner de cookies com opções de **Aceitar todos**, **Recusar opcionais** e **Personalizar**.
|
||||
3.2. Cookies estritamente necessários não podem ser desativados, pois sem eles a sessão e a proteção CSRF deixam de funcionar.
|
||||
3.3. A escolha é armazenada no cookie `city10-consent` e pode ser alterada a qualquer momento em **Preferências de Privacidade** no dashboard.
|
||||
|
||||
## 4. Base Legal
|
||||
Tratamento de cookies não necessários ocorre com base no consentimento (art. 7º, I, LGPD e Marco Civil da Internet, Lei nº 12.965/2014).
|
||||
|
||||
## 5. Exclusão
|
||||
O usuário pode remover os cookies a qualquer momento pelas configurações do navegador, ciente de que recursos personalizados voltarão aos padrões.
|
||||
TXT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cookie Policy body in English.
|
||||
*/
|
||||
private function cookiesEn(): string
|
||||
{
|
||||
return <<<'TXT'
|
||||
# Cookie Policy — City10
|
||||
|
||||
**Version 1.0.0**
|
||||
|
||||
## 1. What cookies are
|
||||
Cookies are small text files stored in the browser during navigation, essential for session handling and preferences.
|
||||
|
||||
## 2. Cookies Used
|
||||
|
||||
| Cookie | Type | Purpose | Duration | Consent |
|
||||
|---|---|---|---|---|
|
||||
| city10-session | Strictly necessary | Keep the authenticated session (CSRF + login) | Session / 120 min | Exempt (necessary) |
|
||||
| XSRF-TOKEN | Strictly necessary | CSRF attack protection | Session | Exempt (necessary) |
|
||||
| city10-locale | Performance | Store the user's chosen language | 1 year | Yes |
|
||||
| city10-consent | Strictly necessary | Record the accepted terms version | 1 year | Exempt (necessary) |
|
||||
| city10-analytics | Analytics | Aggregated usage metrics (future) | 90 days | Yes |
|
||||
|
||||
## 3. Consent Management
|
||||
3.1. On first access the user sees the cookie banner with **Accept all**, **Reject optional** and **Customise** options.
|
||||
3.2. Strictly necessary cookies cannot be disabled because sessions and CSRF protection depend on them.
|
||||
3.3. The choice is stored in the `city10-consent` cookie and can be changed at any time under **Privacy Preferences** in the dashboard.
|
||||
|
||||
## 4. Legal Basis
|
||||
Processing of non-essential cookies relies on consent (art. 7, I, LGPD and the Brazilian Internet Civil Framework, Law nº 12.965/2014).
|
||||
|
||||
## 5. Deletion
|
||||
Users may remove cookies at any time through browser settings, understanding that customised features will return to defaults.
|
||||
TXT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cookie Policy body in Spanish.
|
||||
*/
|
||||
private function cookiesEs(): string
|
||||
{
|
||||
return <<<'TXT'
|
||||
# Política de Cookies — City10
|
||||
|
||||
**Versión 1.0.0**
|
||||
|
||||
## 1. Qué son las cookies
|
||||
Las cookies son pequeños archivos de texto almacenados en el navegador durante la navegación, esenciales para el funcionamiento de sesiones y preferencias.
|
||||
|
||||
## 2. Cookies Utilizadas
|
||||
|
||||
| Cookie | Tipo | Finalidad | Duración | Consentimiento |
|
||||
|---|---|---|---|---|
|
||||
| city10-session | Estrictamente necesario | Mantener la sesión autenticada (CSRF + login) | Sesión / 120 min | Exento (necesario) |
|
||||
| XSRF-TOKEN | Estrictamente necesario | Protección contra ataques CSRF | Sesión | Exento (necesario) |
|
||||
| city10-locale | Rendimiento | Guardar el idioma elegido por el usuario | 1 año | Sí |
|
||||
| city10-consent | Estrictamente necesario | Registrar la versión de los términos aceptados | 1 año | Exento (necesario) |
|
||||
| city10-analytics | Analítico | Métricas agregadas de uso (futuro) | 90 días | Sí |
|
||||
|
||||
## 3. Gestión del Consentimiento
|
||||
3.1. Al acceder a la plataforma, el usuario visualiza el banner de cookies con opciones de **Aceptar todos**, **Rechazar opcionales** y **Personalizar**.
|
||||
3.2. Las cookies estrictamente necesarias no pueden desactivarse, ya que sin ellas la sesión y la protección CSRF dejan de funcionar.
|
||||
3.3. La elección se almacena en la cookie `city10-consent` y puede modificarse en **Preferencias de Privacidad** del dashboard.
|
||||
|
||||
## 4. Base Legal
|
||||
El tratamiento de cookies no necesarias se basa en el consentimiento (art. 7, I, LGPD y Marco Civil de Internet, Ley nº 12.965/2014).
|
||||
|
||||
## 5. Eliminación
|
||||
El usuario puede eliminar las cookies en cualquier momento desde la configuración del navegador, sabiendo que las funciones personalizadas volverán a los valores predeterminados.
|
||||
TXT;
|
||||
}
|
||||
}
|
||||
Executable
+46
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
/**
|
||||
* Seeds the initial platform accounts.
|
||||
*
|
||||
* Creates the administrator and a regular test user with the credentials
|
||||
* provided for the development environment. Passwords are stored using
|
||||
* Laravel's bcrypt hashing through the model's "hashed" cast.
|
||||
*/
|
||||
class UserSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Seed the users table.
|
||||
*/
|
||||
public function run(): void
|
||||
{
|
||||
User::updateOrCreate(
|
||||
['email' => 'marcocosta@gmx.com'],
|
||||
[
|
||||
'name' => 'Marco Costa',
|
||||
'password' => 'Mega886ax',
|
||||
'role' => User::ROLE_ADMIN,
|
||||
'locale' => 'pt_BR',
|
||||
'is_active' => true,
|
||||
'email_verified_at' => now(),
|
||||
],
|
||||
);
|
||||
|
||||
User::updateOrCreate(
|
||||
['email' => 'user@sistema.net'],
|
||||
[
|
||||
'name' => 'Usuário de Testes',
|
||||
'password' => 'User123',
|
||||
'role' => User::ROLE_USER,
|
||||
'locale' => 'pt_BR',
|
||||
'is_active' => true,
|
||||
'email_verified_at' => now(),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user