First Commit

This commit is contained in:
marco
2026-09-01 07:49:48 -03:00
commit 28b1044038
399 changed files with 41659 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
*.sqlite*
+45
View File
@@ -0,0 +1,45 @@
<?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),
];
}
/**
* Indicate that the model's email address should be unverified.
*/
public function unverified(): static
{
return $this->state(fn (array $attributes) => [
'email_verified_at' => null,
]);
}
}
@@ -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,137 @@
<?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
{
$teams = config('permission.teams');
$tableNames = config('permission.table_names');
$columnNames = config('permission.column_names');
$pivotRole = $columnNames['role_pivot_key'] ?? 'role_id';
$pivotPermission = $columnNames['permission_pivot_key'] ?? 'permission_id';
throw_if(empty($tableNames), 'Error: config/permission.php not loaded. Run [php artisan config:clear] and try again.');
throw_if($teams && empty($columnNames['team_foreign_key'] ?? null), 'Error: team_foreign_key on config/permission.php not loaded. Run [php artisan config:clear] and try again.');
/**
* See `docs/prerequisites.md` for suggested lengths on 'name' and 'guard_name' if "1071 Specified key was too long" errors are encountered.
*/
Schema::create($tableNames['permissions'], static function (Blueprint $table) {
$table->id(); // permission id
$table->string('name');
$table->string('guard_name');
$table->timestamps();
$table->unique(['name', 'guard_name']);
});
/**
* See `docs/prerequisites.md` for suggested lengths on 'name' and 'guard_name' if "1071 Specified key was too long" errors are encountered.
*/
Schema::create($tableNames['roles'], static function (Blueprint $table) use ($teams, $columnNames) {
$table->id(); // role id
if ($teams || config('permission.testing')) { // permission.testing is a fix for sqlite testing
$table->unsignedBigInteger($columnNames['team_foreign_key'])->nullable();
$table->index($columnNames['team_foreign_key'], 'roles_team_foreign_key_index');
}
$table->string('name');
$table->string('guard_name');
$table->timestamps();
if ($teams || config('permission.testing')) {
$table->unique([$columnNames['team_foreign_key'], 'name', 'guard_name']);
} else {
$table->unique(['name', 'guard_name']);
}
});
Schema::create($tableNames['model_has_permissions'], static function (Blueprint $table) use ($tableNames, $columnNames, $pivotPermission, $teams) {
$table->unsignedBigInteger($pivotPermission);
$table->string('model_type');
$table->unsignedBigInteger($columnNames['model_morph_key']);
$table->index([$columnNames['model_morph_key'], 'model_type'], 'model_has_permissions_model_id_model_type_index');
$table->foreign($pivotPermission)
->references('id') // permission id
->on($tableNames['permissions'])
->cascadeOnDelete();
if ($teams) {
$table->unsignedBigInteger($columnNames['team_foreign_key']);
$table->index($columnNames['team_foreign_key'], 'model_has_permissions_team_foreign_key_index');
$table->primary([$columnNames['team_foreign_key'], $pivotPermission, $columnNames['model_morph_key'], 'model_type'],
'model_has_permissions_permission_model_type_primary');
} else {
$table->primary([$pivotPermission, $columnNames['model_morph_key'], 'model_type'],
'model_has_permissions_permission_model_type_primary');
}
});
Schema::create($tableNames['model_has_roles'], static function (Blueprint $table) use ($tableNames, $columnNames, $pivotRole, $teams) {
$table->unsignedBigInteger($pivotRole);
$table->string('model_type');
$table->unsignedBigInteger($columnNames['model_morph_key']);
$table->index([$columnNames['model_morph_key'], 'model_type'], 'model_has_roles_model_id_model_type_index');
$table->foreign($pivotRole)
->references('id') // role id
->on($tableNames['roles'])
->cascadeOnDelete();
if ($teams) {
$table->unsignedBigInteger($columnNames['team_foreign_key']);
$table->index($columnNames['team_foreign_key'], 'model_has_roles_team_foreign_key_index');
$table->primary([$columnNames['team_foreign_key'], $pivotRole, $columnNames['model_morph_key'], 'model_type'],
'model_has_roles_role_model_type_primary');
} else {
$table->primary([$pivotRole, $columnNames['model_morph_key'], 'model_type'],
'model_has_roles_role_model_type_primary');
}
});
Schema::create($tableNames['role_has_permissions'], static function (Blueprint $table) use ($tableNames, $pivotRole, $pivotPermission) {
$table->unsignedBigInteger($pivotPermission);
$table->unsignedBigInteger($pivotRole);
$table->foreign($pivotPermission)
->references('id') // permission id
->on($tableNames['permissions'])
->cascadeOnDelete();
$table->foreign($pivotRole)
->references('id') // role id
->on($tableNames['roles'])
->cascadeOnDelete();
$table->primary([$pivotPermission, $pivotRole], 'role_has_permissions_permission_id_role_id_primary');
});
app('cache')
->store(config('permission.cache.store') != 'default' ? config('permission.cache.store') : null)
->forget(config('permission.cache.key'));
}
/**
* Reverse the migrations.
*/
public function down(): void
{
$tableNames = config('permission.table_names');
throw_if(empty($tableNames), 'Error: config/permission.php not found and defaults could not be merged. Please publish the package configuration before proceeding, or drop the tables manually.');
Schema::dropIfExists($tableNames['role_has_permissions']);
Schema::dropIfExists($tableNames['model_has_roles']);
Schema::dropIfExists($tableNames['model_has_permissions']);
Schema::dropIfExists($tableNames['roles']);
Schema::dropIfExists($tableNames['permissions']);
}
};
@@ -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('subjects', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->nullable()->constrained()->nullOnDelete();
$table->string('name');
$table->text('description')->nullable();
$table->text('introduction')->nullable();
$table->boolean('is_system')->default(false);
$table->boolean('is_active')->default(true);
$table->timestamps();
$table->index(['user_id', 'is_system']);
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('subjects');
}
};
@@ -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('user_subjects', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
$table->foreignId('subject_id')->constrained()->cascadeOnDelete();
$table->boolean('is_active')->default(true);
$table->integer('frequency_days')->default(7);
$table->time('preferred_time')->nullable();
$table->json('weekdays')->nullable();
$table->timestamps();
$table->unique(['user_id', 'subject_id']);
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('user_subjects');
}
};
@@ -0,0 +1,37 @@
<?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('insights', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
$table->foreignId('subject_id')->constrained()->cascadeOnDelete();
$table->text('content');
$table->string('type')->default('insight');
$table->json('metadata')->nullable();
$table->integer('input_tokens')->default(0);
$table->integer('output_tokens')->default(0);
$table->timestamp('sent_at')->nullable();
$table->timestamps();
$table->index(['user_id', 'subject_id', 'created_at']);
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('insights');
}
};
@@ -0,0 +1,36 @@
<?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('insight_schedules', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
$table->foreignId('subject_id')->constrained()->cascadeOnDelete();
$table->string('frequency')->default('weekly');
$table->json('weekdays')->nullable();
$table->time('preferred_time')->nullable();
$table->boolean('is_active')->default(true);
$table->timestamp('last_sent_at')->nullable();
$table->timestamps();
$table->unique(['user_id', 'subject_id']);
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('insight_schedules');
}
};
@@ -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('telegram_users', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
$table->string('telegram_user_id')->unique();
$table->string('username')->nullable();
$table->string('first_name')->nullable();
$table->string('last_name')->nullable();
$table->boolean('is_active')->default(true);
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('telegram_users');
}
};
@@ -0,0 +1,32 @@
<?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('token_usages', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
$table->unsignedInteger('input_tokens')->default(0);
$table->unsignedInteger('output_tokens')->default(0);
$table->unsignedInteger('total_tokens')->default(0);
$table->string('model')->nullable();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('token_usages');
}
};
@@ -0,0 +1,31 @@
<?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::table('users', function (Blueprint $table) {
$table->string('phone')->nullable()->after('email');
$table->string('locale')->default('pt_BR')->after('phone');
$table->boolean('is_active')->default(true)->after('locale');
$table->boolean('is_approved')->default(false)->after('is_active');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('users', function (Blueprint $table) {
$table->dropColumn(['phone', 'locale', 'is_active', 'is_approved']);
});
}
};
@@ -0,0 +1,30 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('user_consents', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
$table->string('terms_version');
$table->string('privacy_version');
$table->string('cookie_version');
$table->timestamp('accepted_at');
$table->string('ip_address')->nullable();
$table->text('user_agent')->nullable();
$table->timestamps();
$table->index(['user_id', 'accepted_at']);
});
}
public function down(): void
{
Schema::dropIfExists('user_consents');
}
};
@@ -0,0 +1,38 @@
<?php
declare(strict_types=1);
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
/**
* Migration to rename frequency_days to frequency in user_subjects table.
* This ensures consistency between the model and the controller.
*/
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('user_subjects', function (Blueprint $table) {
if (Schema::hasColumn('user_subjects', 'frequency_days')) {
$table->renameColumn('frequency_days', 'frequency');
}
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('user_subjects', function (Blueprint $table) {
if (Schema::hasColumn('user_subjects', 'frequency')) {
$table->renameColumn('frequency', 'frequency_days');
}
});
}
};
@@ -0,0 +1,34 @@
<?php
declare(strict_types=1);
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
/**
* Migration to change the frequency column type from integer to string.
* This allows storing frequency values like 'daily', 'weekly', 'biweekly', 'monthly'.
*/
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('user_subjects', function (Blueprint $table) {
$table->string('frequency', 50)->change();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('user_subjects', function (Blueprint $table) {
$table->integer('frequency')->change();
});
}
};
@@ -0,0 +1,50 @@
<?php
declare(strict_types=1);
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
/**
* Migration to change preferred_time from single time to JSON array of times.
* This allows users to set multiple delivery times per subject.
*/
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('user_subjects', function (Blueprint $table) {
$table->json('preferred_times')->nullable()->after('frequency');
});
// Migrate existing data: convert single preferred_time to JSON array
DB::table('user_subjects')->whereNotNull('preferred_time')->update([
'preferred_times' => DB::raw("JSON_ARRAY(preferred_time)"),
]);
Schema::table('user_subjects', function (Blueprint $table) {
$table->dropColumn('preferred_time');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('user_subjects', function (Blueprint $table) {
$table->time('preferred_time')->nullable()->after('frequency');
});
// Restore data: convert first element of JSON array back to single time
DB::statement("UPDATE user_subjects SET preferred_time = JSON_UNQUOTE(JSON_EXTRACT(preferred_times, '$[0]')) WHERE preferred_times IS NOT NULL");
Schema::table('user_subjects', function (Blueprint $table) {
$table->dropColumn('preferred_times');
});
}
};
@@ -0,0 +1,36 @@
<?php
declare(strict_types=1);
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
/**
* Migration to add bot_token and bot_username fields to telegram_users table.
* This allows each user to configure their own Telegram bot.
*/
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('telegram_users', function (Blueprint $table) {
$table->string('bot_token')->nullable()->after('telegram_user_id');
$table->string('bot_username')->nullable()->after('bot_token');
$table->string('chat_id')->nullable()->after('bot_username');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('telegram_users', function (Blueprint $table) {
$table->dropColumn(['bot_token', 'bot_username', 'chat_id']);
});
}
};
@@ -0,0 +1,38 @@
<?php
declare(strict_types=1);
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
/**
* Migration to rename username column to telegram_username in telegram_users table.
* Ensures consistency with the model's fillable attributes.
*/
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('telegram_users', function (Blueprint $table) {
$table->renameColumn('username', 'telegram_username');
$table->renameColumn('first_name', 'telegram_first_name');
$table->renameColumn('last_name', 'telegram_last_name');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('telegram_users', function (Blueprint $table) {
$table->renameColumn('telegram_username', 'username');
$table->renameColumn('telegram_first_name', 'first_name');
$table->renameColumn('telegram_last_name', 'last_name');
});
}
};
@@ -0,0 +1,37 @@
<?php
declare(strict_types=1);
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
/**
* Migration to add provider column to token_usages table.
*/
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('token_usages', function (Blueprint $table) {
if (!Schema::hasColumn('token_usages', 'provider')) {
$table->string('provider', 50)->nullable()->after('model');
}
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('token_usages', function (Blueprint $table) {
if (Schema::hasColumn('token_usages', 'provider')) {
$table->dropColumn('provider');
}
});
}
};
@@ -0,0 +1,34 @@
<?php
declare(strict_types=1);
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
/**
* Migration to add language field to user_subjects table.
* Allows users to choose the language for AI-generated insights per subject.
*/
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('user_subjects', function (Blueprint $table) {
$table->string('insight_language', 10)->nullable()->after('weekdays');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('user_subjects', function (Blueprint $table) {
$table->dropColumn('insight_language');
});
}
};
@@ -0,0 +1,34 @@
<?php
declare(strict_types=1);
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
/**
* Migration to add delivery_channels field to user_subjects table.
* Allows users to choose where to receive insights (dashboard, telegram, etc.).
*/
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('user_subjects', function (Blueprint $table) {
$table->json('delivery_channels')->nullable()->after('insight_language');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('user_subjects', function (Blueprint $table) {
$table->dropColumn('delivery_channels');
});
}
};
@@ -0,0 +1,34 @@
<?php
declare(strict_types=1);
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
/**
* Migration to add last_sent_at column to user_subjects table.
* Tracks when the last insight was sent for scheduling purposes.
*/
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('user_subjects', function (Blueprint $table) {
$table->timestamp('last_sent_at')->nullable()->after('delivery_channels');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('user_subjects', function (Blueprint $table) {
$table->dropColumn('last_sent_at');
});
}
};
@@ -0,0 +1,34 @@
<?php
declare(strict_types=1);
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
/**
* Migration to add token_limit field to users table.
* SuperAdmin can set the maximum token usage per user.
*/
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('users', function (Blueprint $table) {
$table->bigInteger('token_limit')->nullable()->after('is_approved');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('users', function (Blueprint $table) {
$table->dropColumn('token_limit');
});
}
};
@@ -0,0 +1,34 @@
<?php
declare(strict_types=1);
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
/**
* Migration to add timezone field to users table.
* Allows users to set their local timezone for scheduling insights.
*/
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('users', function (Blueprint $table) {
$table->string('timezone', 50)->nullable()->after('locale');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('users', function (Blueprint $table) {
$table->dropColumn('timezone');
});
}
};
@@ -0,0 +1,30 @@
<?php
declare(strict_types=1);
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('subscription_plans', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->text('description')->nullable();
$table->decimal('price', 10, 2);
$table->string('currency', 3)->default('BRL');
$table->integer('duration_days')->default(30);
$table->integer('token_limit')->nullable();
$table->boolean('is_active')->default(true);
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('subscription_plans');
}
};
@@ -0,0 +1,30 @@
<?php
declare(strict_types=1);
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('user_subscriptions', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
$table->foreignId('subscription_plan_id')->constrained()->cascadeOnDelete();
$table->timestamp('start_date');
$table->timestamp('end_date');
$table->string('status')->default('active'); // active, expired, cancelled, suspended
$table->boolean('payment_approved')->default(false);
$table->timestamp('cancelled_at')->nullable();
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('user_subscriptions');
}
};
@@ -0,0 +1,32 @@
<?php
declare(strict_types=1);
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('invoices', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
$table->foreignId('user_subscription_id')->constrained()->cascadeOnDelete();
$table->string('invoice_number')->unique();
$table->decimal('amount', 10, 2);
$table->string('currency', 3)->default('BRL');
$table->string('status')->default('pending'); // pending, paid, cancelled, refunded
$table->string('payment_method')->nullable();
$table->timestamp('paid_at')->nullable();
$table->string('pdf_path')->nullable();
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('invoices');
}
};
@@ -0,0 +1,29 @@
<?php
declare(strict_types=1);
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('user_notifications', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
$table->string('type'); // payment_due, payment_overdue, subscription_expiring, subscription_expired, payment_received, admin_message
$table->string('title');
$table->text('message')->nullable();
$table->boolean('is_read')->default(false);
$table->timestamp('read_at')->nullable();
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('user_notifications');
}
};
@@ -0,0 +1,24 @@
<?php
declare(strict_types=1);
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::table('telegram_users', function (Blueprint $table) {
$table->dropUnique('telegram_users_telegram_user_id_unique');
});
}
public function down(): void
{
Schema::table('telegram_users', function (Blueprint $table) {
$table->unique('telegram_user_id');
});
}
};
@@ -0,0 +1,29 @@
<?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::table('subjects', function (Blueprint $table) {
$table->string('message_type')->nullable()->after('introduction');
$table->text('custom_instructions')->nullable()->after('message_type');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('subjects', function (Blueprint $table) {
$table->dropColumn(['message_type', 'custom_instructions']);
});
}
};
@@ -0,0 +1,31 @@
<?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('user_interests', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
$table->string('interest');
$table->timestamps();
$table->index('user_id');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('user_interests');
}
};
@@ -0,0 +1,37 @@
<?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('chat_messages', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
$table->text('message');
$table->text('response')->nullable();
$table->string('provider')->nullable();
$table->string('model')->nullable();
$table->integer('input_tokens')->default(0);
$table->integer('output_tokens')->default(0);
$table->timestamp('sent_at')->nullable();
$table->timestamps();
$table->index(['user_id', 'created_at']);
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('chat_messages');
}
};
@@ -0,0 +1,30 @@
<?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('settings', function (Blueprint $table) {
$table->id();
$table->string('key')->unique();
$table->text('value')->nullable();
$table->string('type')->default('string');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('settings');
}
};
@@ -0,0 +1,53 @@
<?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('custom_subjects', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
$table->string('name');
$table->string('pdf_path');
$table->text('extracted_content')->nullable();
$table->string('processing_status')->default('pending');
$table->text('error_message')->nullable();
$table->boolean('is_active')->default(true);
$table->timestamps();
$table->index(['user_id', 'is_active']);
});
Schema::create('custom_subject_questions', function (Blueprint $table) {
$table->id();
$table->foreignId('custom_subject_id')->constrained()->cascadeOnDelete();
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
$table->text('question');
$table->text('answer')->nullable();
$table->string('provider')->nullable();
$table->string('model')->nullable();
$table->integer('input_tokens')->default(0);
$table->integer('output_tokens')->default(0);
$table->timestamp('asked_at');
$table->timestamps();
$table->index(['custom_subject_id', 'created_at']);
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('custom_subject_questions');
Schema::dropIfExists('custom_subjects');
}
};
+97
View File
@@ -0,0 +1,97 @@
<?php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
use App\Models\User;
use App\Models\Subject;
use Spatie\Permission\Models\Role;
use Spatie\Permission\Models\Permission;
class DatabaseSeeder extends Seeder
{
public function run(): void
{
$superAdminRole = Role::firstOrCreate(["name" => "superadmin"]);
$adminRole = Role::firstOrCreate(["name" => "admin"]);
$superAdmin = User::firstOrCreate(
["email" => "marcocosta@gmx.com"],
[
"name" => "Super Admin",
"password" => bcrypt("superadmin123456"),
"is_active" => true,
"is_approved" => true,
]
);
$superAdmin->assignRole("superadmin");
$subjects = [
[
"name" => "Aprendizado e Treinamento de Inglês",
"description" => "Learn English language",
"introduction" => "O Inglês é o idioma mais falado no mundo dos negócios, tecnologia e ciência. Com mais de 1,5 bilhão de falantes globais, dominar o Inglês abre portas para oportunidades internacionais, acesso a conteúdo original e comunicação efetiva em qualquer lugar do mundo. Este treinamento cobre desde fundamentos básicos até técnicas avançadas de fluência.",
"is_system" => true,
],
[
"name" => "Aprendizado e Treinamento de Flight Simulator X",
"description" => "Learn Flight Simulator X",
"introduction" => "Microsoft Flight Simulator X é um dos simuladores de voo mais icônicos da história da aviação virtual. Lançado em 2006, ainda é amplamente utilizado por entusiastas e pilotos reais para treinamento de procedimentos, navegação e familiarização com aeronaves. Este treinamento cobre desde o básico até técnicas avançadas de voo por instrumentos.",
"is_system" => true,
],
[
"name" => "Aprendizado e Treinamento de Administração de Pequenos Negócios",
"description" => "Learn small business administration",
"introduction" => "Administrar um pequeno negócio requer conhecimento multidisciplinar que vai desde gestão financeira até marketing digital. Pequenas empresas representam mais de 90% dos negócios no Brasil e são responsáveis por grande parte dos empregos. Este treinamento aborda planejamento estratégico, controle financeiro, gestão de pessoas e crescimento sustentável.",
"is_system" => true,
],
[
"name" => "Aprendizado e Treinamento de Estoicismo",
"description" => "Learn Stoicism",
"introduction" => "O Estoicismo é uma filosofia antiga fundada em Atenas por Zenão de Cítio por volta de 300 a.C. Ensinando resiliência emocional, virtude e aceitação do que não podemos controlar, o Estoicismo influenciou pensadores como Sêneca, Epicteto e Marco Aurélio. Seus princípios são cada vez mais aplicados na psicologia moderna e no desenvolvimento pessoal.",
"is_system" => true,
],
[
"name" => "Aprendizado e Treinamento de Eletrônica",
"description" => "Learn Electronics",
"introduction" => "A Eletrônica é a base da tecnologia moderna, desde smartphones até sistemas espaciais. Compreender componentes como resistores, capacitores, transistores e circuitos integrados é essencial para qualquer profissional de tecnologia. Este treinamento cobre desde conceitos fundamentais de corrente e tensão até projetos práticos com microcontroladores.",
"is_system" => true,
],
[
"name" => "Aprendizado e Treinamento de Mandarim",
"description" => "Learn Mandarin",
"introduction" => "O Mandarim é o idioma mais falado do mundo com mais de 1 bilhão de falantes nativos. Como língua oficial da China, potência econômica global, aprender Mandarim abre oportunidades únicas nos negócios, diplomacia e cultura. Diferente dos idiomas ocidentais, o Mandarim usa tons musicais e caracteres pictográficos que exigem abordagens específicas de aprendizado.",
"is_system" => true,
],
[
"name" => "Aprendizado e Treinamento de As Leis do Triunfo",
"description" => "Learn The Laws of Success by Napoleon Hill",
"introduction" => "As Leis do Triunfo, de Napoleon Hill, é uma das obras mais influentes sobre sucesso e desenvolvimento pessoal já escritas. Publicado originalmente em 1928 após mais de 20 anos de pesquisa com mais de 500 milionários, o livro apresenta 16 princípios universais do sucesso. Hill identificou que pessoas de sucesso compartilham características comuns, independentemente de área ou época. Os princípios incluem: Mentalidade de Sucesso, Propósito Definido, Confiança em Si Mesmo, Hábitos de Economia, Iniciativa e Liderança, Imaginação, Entusiasmo, Autocontrole, O Hábito de Fazer Mais do que o Obrigado, Personalidade Atraente, Pensamento preciso, Concentração, cooperação, fracasso como trampolim, tolerância ao ouro (equilíbrio financeiro), e a Lei das Causas e Efeitos. Cada insight gerado explorará um destes princípios com exemplos práticos, citações do livro e aplicações contemporâneas para sua vida pessoal e profissional.",
"is_system" => true,
],
[
"name" => "Aprendizado e Treinamento de Gurus dos Negócios",
"description" => "Learn from business gurus: ideas, quotes, negotiation and management strategies",
"introduction" => "Os Gurus dos Negócios são pensadores e líderes que transformaram a forma como organizações e indivíduos entendem administração, negociação, liderança e estratégia. Este assunto explora as ideias centrais de figuras como Peter Drucker (gestão por objetivos e eficácia), Jim Collins (níveis de liderança e o efeito flywheel), Michael Porter (vantagem competitiva e estratégia), Clayton Christensen (inovação disruptiva), Philip Kotler (marketing moderno e troca de valor), Gary Vaynerchuk (atenção como moeda e personal branding), Robert Kiyosaki (inteligência financeira e ativos vs passivos), e Simon Sinek (círculo dourado e propósito). Cada insight apresenta frases marcantes, métodos de negociação (como o método Harvard de negociação baseado em interesses e não em posições), modelos administrativos (OKR, Lean Startup, Balanced Scorecard) e aplicações práticas para empreendedores, gestores e profissionais que desejam elevar seu desempenho nos negócios.",
"is_system" => true,
],
[
"name" => "Aprendizado e Treinamento de Gurus dos Negócios e Riqueza",
"description" => "Ideias, frases, modos de negociar, administrar, conquistar bem-estar financeiro, riqueza, mente milionária, arte de negociar e lei da atração com base em gurus e referências do mundo dos negócios.",
"introduction" => "Este assunto é um mergulho profundo no universo dos gurus da riqueza e dos negócios, reunindo ideias, frases marcantes, modos de negociar, métodos de administrar, caminhos para o bem-estar financeiro, construção de riqueza, desenvolvimento da mente milionária, a arte de negociar e os princípios da lei da atração aplicados aos negócios. Você encontrará conteúdos baseados em referências como Napoleon Hill (Lei do Sucesso e pensamento positivo), T. Harv Eker (códigos de riqueza da mente milionária), Tony Robbins (planejamento financeiro e estado de excelência), Robert Kiyosaki (ativos vs passivos e quadrantes do fluxo de caixa), Donald Trump (negociação implacável e valorização de ativos), Grant Cardone (10X e disciplina comercial), Jordan Belfort (arte de persuadir e vender), Brian Tracy (metas, foco e prosperidade), Jack Canfield (lei da atração e prosperidade), Joe Vitale (marketing espiritual e atração de clientes), Deepak Chopra (abundância e alinhamento interior), Rhonda Byrne (O Segredo e visualização criativa), Dale Carnegie (relacionamentos e influência), Zig Ziglar (vendas e motivação), entre outros. Cada insight explora um princípio, uma técnica ou uma história prática que pode ser aplicada para acelerar resultados financeiros, melhorar negociações, fortalecer a mentalidade de abundância e construir um negócio ou carreira mais próspera.",
"is_system" => true,
],
];
foreach ($subjects as $subjectData) {
$subject = Subject::firstOrCreate(
["name" => $subjectData["name"]],
array_merge($subjectData, ["user_id" => null])
);
// Update existing subjects with new data
if (!$subject->wasRecentlyCreated) {
$subject->update($subjectData);
}
}
}
}
+50
View File
@@ -0,0 +1,50 @@
<?php
declare(strict_types=1);
namespace Database\Seeders;
use App\Models\SubscriptionPlan;
use Illuminate\Database\Seeder;
class PlanSeeder extends Seeder
{
public function run(): void
{
SubscriptionPlan::firstOrCreate(
['name' => 'Plano Básico Mensal'],
[
'description' => 'Acesso completo a todos os recursos do Annunk por 30 dias. Ideal para quem está começando.',
'price' => 29.90,
'currency' => 'BRL',
'duration_days' => 30,
'token_limit' => 50000,
'is_active' => true,
]
);
SubscriptionPlan::firstOrCreate(
['name' => 'Plano Premium Mensal'],
[
'description' => 'Acesso completo com limite de tokens estendido e suporte prioritário.',
'price' => 59.90,
'currency' => 'BRL',
'duration_days' => 30,
'token_limit' => 200000,
'is_active' => true,
]
);
SubscriptionPlan::firstOrCreate(
['name' => 'Plano Anual Premium'],
[
'description' => 'O melhor custo-benefício! Acesso completo por 12 meses com limite de tokens generoso.',
'price' => 399.90,
'currency' => 'BRL',
'duration_days' => 365,
'token_limit' => 500000,
'is_active' => true,
]
);
}
}