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:
2026-10-17 20:46:15 -03:00
commit 79d12fa2f4
128 changed files with 17053 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
*.sqlite*
+68
View File
@@ -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');
}
};
@@ -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');
}
};
@@ -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');
}
};
+128
View File
@@ -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,
],
);
}
}
+29
View File
@@ -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,
]);
}
}
+442
View File
@@ -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;
}
}
+46
View File
@@ -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(),
],
);
}
}