diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php
index 303b6ec..837bab0 100644
--- a/app/Providers/AppServiceProvider.php
+++ b/app/Providers/AppServiceProvider.php
@@ -5,6 +5,7 @@ namespace App\Providers;
use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\RateLimiter;
+use Illuminate\Support\Facades\Schema;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
@@ -16,6 +17,8 @@ class AppServiceProvider extends ServiceProvider
public function boot(): void
{
+ Schema::defaultStringLength(191);
+
RateLimiter::for('collect', function (Request $request) {
return Limit::perMinute(120)->by($request->ip());
});
diff --git a/compose.yaml b/compose.yaml
new file mode 100644
index 0000000..379eb37
--- /dev/null
+++ b/compose.yaml
@@ -0,0 +1,56 @@
+services:
+ laravel.test:
+ build:
+ context: ./vendor/laravel/sail/runtimes/8.5
+ dockerfile: Dockerfile
+ args:
+ WWWGROUP: '${WWWGROUP}'
+ image: sail-8.5/app
+ extra_hosts:
+ - 'host.docker.internal:host-gateway'
+ ports:
+ - '${APP_PORT:-80}:80'
+ - '${VITE_PORT:-5173}:${VITE_PORT:-5173}'
+ environment:
+ WWWUSER: '${WWWUSER}'
+ LARAVEL_SAIL: 1
+ XDEBUG_MODE: '${SAIL_XDEBUG_MODE:-off}'
+ XDEBUG_CONFIG: '${SAIL_XDEBUG_CONFIG:-client_host=host.docker.internal}'
+ IGNITION_LOCAL_SITES_PATH: '${PWD}'
+ volumes:
+ - '.:/var/www/html'
+ networks:
+ - sail
+ depends_on:
+ - mysql
+ mysql:
+ image: 'mysql:8.4'
+ ports:
+ - '${FORWARD_DB_PORT:-3306}:3306'
+ environment:
+ MYSQL_ROOT_PASSWORD: '${DB_PASSWORD}'
+ MYSQL_ROOT_HOST: '%'
+ MYSQL_DATABASE: '${DB_DATABASE}'
+ MYSQL_USER: '${DB_USERNAME}'
+ MYSQL_PASSWORD: '${DB_PASSWORD}'
+ MYSQL_ALLOW_EMPTY_PASSWORD: 1
+ MYSQL_EXTRA_OPTIONS: '${MYSQL_EXTRA_OPTIONS:-}'
+ volumes:
+ - 'sail-mysql:/var/lib/mysql'
+ - './vendor/laravel/sail/database/mysql/create-testing-database.sh:/docker-entrypoint-initdb.d/10-create-testing-database.sh'
+ networks:
+ - sail
+ healthcheck:
+ test:
+ - CMD
+ - mysqladmin
+ - ping
+ - '-p${DB_PASSWORD}'
+ retries: 3
+ timeout: 5s
+networks:
+ sail:
+ driver: bridge
+volumes:
+ sail-mysql:
+ driver: local
diff --git a/config/database.php b/config/database.php
index 64709ce..ec2f93c 100644
--- a/config/database.php
+++ b/config/database.php
@@ -58,7 +58,7 @@ return [
'prefix' => '',
'prefix_indexes' => true,
'strict' => true,
- 'engine' => null,
+ 'engine' => env('DB_ENGINE', 'InnoDB'),
'options' => extension_loaded('pdo_mysql') ? array_filter([
(PHP_VERSION_ID >= 80500 ? Mysql::ATTR_SSL_CA : PDO::MYSQL_ATTR_SSL_CA) => env('MYSQL_ATTR_SSL_CA'),
]) : [],
diff --git a/database/migrations/2026_03_17_000004_create_page_views_table.php b/database/migrations/2026_03_17_000004_create_page_views_table.php
index 86fdeb2..5171fca 100644
--- a/database/migrations/2026_03_17_000004_create_page_views_table.php
+++ b/database/migrations/2026_03_17_000004_create_page_views_table.php
@@ -3,6 +3,7 @@
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
+use Illuminate\Support\Facades\DB;
return new class extends Migration
{
@@ -22,8 +23,10 @@ return new class extends Migration
$table->timestamps();
$table->index(['website_id', 'viewed_at']);
- $table->index('path');
});
+
+ // Index only the first 255 characters of path to avoid key length issues
+ DB::statement('ALTER TABLE page_views ADD INDEX page_views_path_index (path(255))');
}
public function down(): void
diff --git a/database/migrations/2026_03_19_000002_create_page_daily_aggregates_table.php b/database/migrations/2026_03_19_000002_create_page_daily_aggregates_table.php
index f9a8459..119c3ab 100644
--- a/database/migrations/2026_03_19_000002_create_page_daily_aggregates_table.php
+++ b/database/migrations/2026_03_19_000002_create_page_daily_aggregates_table.php
@@ -3,6 +3,7 @@
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
+use Illuminate\Support\Facades\DB;
return new class extends Migration
{
@@ -19,9 +20,11 @@ return new class extends Migration
$table->unsignedInteger('bounces')->default(0);
$table->timestamps();
- $table->unique(['website_id', 'date', 'path']);
$table->index(['website_id', 'date']);
});
+
+ // Create unique constraint with prefix length on path to avoid key length issues
+ DB::statement('ALTER TABLE page_daily_aggregates ADD UNIQUE page_daily_aggregates_website_id_date_path_unique (website_id, date, path(255))');
}
public function down(): void
diff --git a/database/migrations/2026_03_19_000003_add_performance_indexes.php b/database/migrations/2026_03_19_000003_add_performance_indexes.php
index 8f62435..9946fde 100644
--- a/database/migrations/2026_03_19_000003_add_performance_indexes.php
+++ b/database/migrations/2026_03_19_000003_add_performance_indexes.php
@@ -3,6 +3,7 @@
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
+use Illuminate\Support\Facades\DB;
return new class extends Migration
{
@@ -16,10 +17,12 @@ return new class extends Migration
Schema::table('analytics_sessions', function (Blueprint $table) {
$table->index('visitor_id', 'sess_visitor_id_idx');
$table->index(['website_id', 'is_bounce', 'started_at'], 'sess_bounce_lookup_idx');
- $table->index(['website_id', 'entry_page'], 'sess_entry_page_idx');
- $table->index(['website_id', 'exit_page'], 'sess_exit_page_idx');
});
+ // Add indexes with prefix length on TEXT columns to avoid key length issues
+ DB::statement('ALTER TABLE analytics_sessions ADD INDEX sess_entry_page_idx (website_id, entry_page(255))');
+ DB::statement('ALTER TABLE analytics_sessions ADD INDEX sess_exit_page_idx (website_id, exit_page(255))');
+
Schema::table('events', function (Blueprint $table) {
$table->index('visitor_id', 'ev_visitor_id_idx');
});
@@ -39,10 +42,11 @@ return new class extends Migration
Schema::table('analytics_sessions', function (Blueprint $table) {
$table->dropIndex('sess_visitor_id_idx');
$table->dropIndex('sess_bounce_lookup_idx');
- $table->dropIndex('sess_entry_page_idx');
- $table->dropIndex('sess_exit_page_idx');
});
+ DB::statement('ALTER TABLE analytics_sessions DROP INDEX sess_entry_page_idx');
+ DB::statement('ALTER TABLE analytics_sessions DROP INDEX sess_exit_page_idx');
+
Schema::table('events', function (Blueprint $table) {
$table->dropIndex('ev_visitor_id_idx');
});
diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php
index 6b901f8..ab75279 100644
--- a/database/seeders/DatabaseSeeder.php
+++ b/database/seeders/DatabaseSeeder.php
@@ -20,6 +20,7 @@ class DatabaseSeeder extends Seeder
User::factory()->create([
'name' => 'Test User',
'email' => 'test@example.com',
+ 'password' => bcrypt('password'),
]);
}
}
diff --git a/phpunit.xml b/phpunit.xml
index e7f0a48..a85936a 100644
--- a/phpunit.xml
+++ b/phpunit.xml
@@ -23,8 +23,7 @@
-
-
+