diff --git a/.env.local.example b/.env.local.example index 24af1f2f58..4fdc52be81 100644 --- a/.env.local.example +++ b/.env.local.example @@ -7,14 +7,20 @@ APP_KEY= LOG_STACK=single LOG_DEPRECATIONS_CHANNEL=single -DB_DATABASE=coolify-db -DB_HOST=host.docker.internal -DB_PORT=5432 -DB_USERNAME=coolify -DB_PASSWORD=password +APP_MAINTENANCE_DRIVER=file +CACHE_STORE=file +QUEUE_CONNECTION=sync -REDIS_USERNAME=coolify -REDIS_PASSWORD=password +SESSION_DRIVER=cookie + +DB_CONNECTION=sqlite +# DB_HOST=host.docker.internal +# DB_PORT=5432 +# DB_USERNAME=coolify +# DB_PASSWORD=password + +# REDIS_USERNAME=coolify +# REDIS_PASSWORD=password RAY_ENABLED=true # RAY_PORT= diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c6c37bac0..9638671506 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,9 @@ All notable changes to this project will be documented in this file. ### Breaking Changes -- +Most breaking changes are automatically handled by the upgrade migration. See the [v5 Upgrade Guide](docs) for details. + +- Database primary keys unified from two columns (numeric `id` + CUIDv2 `uuid`) into a single ULID `id` column ### Security @@ -17,6 +19,8 @@ All notable changes to this project will be documented in this file. ### Added - Complete redesign of the Coolify User Interface and User Experience +- Separate notification email in addition to the user's account email +- "Remember me" option on login, keeping users authenticated for 14 days (without it, sessions expire after 24 hours of inactivity) - **v4 to v5 upgrade migration** - Coolify v4 database as `old_pgsql` connection - @@ -31,7 +35,7 @@ All notable changes to this project will be documented in this file. - - **Laravel Configurations** - Hashing algorithm from `bcrypt` to `argon2id` for enhanced security - - Session driver to Redis with inactive sessions expiring after 24h (previously 14 days) + - Session driver to Redis (for proper TTL) with inactive sessions expiring after 24h (previously 14 days) - Encrypted user session data - Password reset token expiration from 60 minutes to 10 minutes - Jobs dispatch only after all DB transactions complete, preventing race conditions @@ -64,9 +68,9 @@ All notable changes to this project will be documented in this file. ### Refactored -- All database migrations for a cleaner, more consistent and stable database schema -- All database models for improved maintainability -- Replace hardcoded queue strings with a `ProcessingQueue` enum +- All database migrations for a cleaner, more consistent and stable database schema (no more `down()` methods, no more defaults in the DB...) +- All database models for improved maintainability, consistency and database interoperability (SQLite and Postgres) +- Hardcoded queue strings replaced with a `ProcessingQueue` enum - `config/constants.php` to `config/coolify.php` for all Coolify-specific settings - Environment variable naming to be shorter and more consistent diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 61ab1e98f0..6e03a565c3 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -8,10 +8,10 @@ 2. Create a new branch or create a branch with the same name as one of the upstream branches 3. Sync your fork with the upstream branches 4. Clone and checkout the code locally -5. Copy `.env.example` to a new `.env` file (no changes should be needed) +5. Copy `.env.local.example` to a new `.env` file (no changes should be needed) 6. As the v5 docker architecture is not ready yet, you need to install PHP, composer and bun locally - 6.1. Install PHP, composer and the laravel installer [https://laravel.com/docs/12.x/installation#installing-php](https://laravel.com/docs/12.x/installation#installing-php) - 6.2. Install bun [https://bun.com/docs/installation](https://bun.com/docs/installation) + 6.1 Install PHP, composer and the laravel installer [https://laravel.com/docs/13.x/installation#installing-php](https://laravel.com/docs/13.x/installation#installing-php) + 6.2 Install bun [https://bun.com/docs/installation](https://bun.com/docs/installation) 7. Run `composer install` to install PHP dependencies 8. Run `bun install` to install JS dependencies 9. Run `composer run dev` to create an sqlite database, generate an APP_KEY, migrate the database and run the development server diff --git a/app/Models/User.php b/app/Models/User.php index 07de52292b..5c8e2620d9 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -5,45 +5,48 @@ namespace App\Models; use Carbon\CarbonInterface; +use Database\Factories\UserFactory; +use Illuminate\Database\Eloquent\Attributes\Hidden; +use Illuminate\Database\Eloquent\Concerns\HasUlids; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Foundation\Auth\User as Authenticatable; /** - * @property-read int $id + * @property-read string $id * @property-read string $name * @property-read string $email + * @property-read string|null $notification_email * @property-read CarbonInterface|null $email_verified_at + * @property-read CarbonInterface|null $notification_email_verified_at * @property-read string $password + * @property-read string|null $remember_token + * @property-read string|null $two_factor_secret + * @property-read string|null $two_factor_recovery_codes + * @property-read CarbonInterface|null $two_factor_confirmed_at * @property-read CarbonInterface $created_at * @property-read CarbonInterface $updated_at */ +#[Hidden(['password', 'remember_token', 'two_factor_secret', 'two_factor_recovery_codes'])] final class User extends Authenticatable { - /** @use HasFactory<\Database\Factories\UserFactory> */ + /** @use HasFactory */ use HasFactory; + use HasUlids; - /** - * The attributes that should be hidden for serialization. - * - * @var list - */ - protected $hidden = [ - 'password', - ]; - - /** - * Get the attributes that should be cast. - * - * @return array - */ protected function casts(): array { return [ - 'id' => 'int', + 'id' => 'string', 'name' => 'string', 'email' => 'string', + 'notification_email' => 'string', 'email_verified_at' => 'datetime', + 'notification_email_verified_at' => 'datetime', 'password' => 'hashed', + 'remember_token' => 'string', + 'two_factor_secret' => 'encrypted', + 'two_factor_recovery_codes' => 'encrypted', + 'two_factor_confirmed_at' => 'datetime', 'created_at' => 'datetime', 'updated_at' => 'datetime', ]; diff --git a/config/app.php b/config/app.php index 938c995b8f..a4b39988e4 100644 --- a/config/app.php +++ b/config/app.php @@ -136,6 +136,6 @@ 'maintenance' => [ 'driver' => env('APP_MAINTENANCE_DRIVER', 'cache'), - 'store' => env('APP_MAINTENANCE_STORE', 'database'), + 'store' => env('APP_MAINTENANCE_STORE', 'redis'), ], ]; diff --git a/config/auth.php b/config/auth.php index 26f0352b4c..c16843c486 100644 --- a/config/auth.php +++ b/config/auth.php @@ -42,6 +42,7 @@ 'web' => [ 'driver' => 'session', 'provider' => 'users', + 'remember' => 14 * 24 * 60, ], ], diff --git a/database/migrations/2025_12_10_154900_create_job_batches_table.php b/database/migrations/2026_03_29_212253_create_job_batches_table.php similarity index 94% rename from database/migrations/2025_12_10_154900_create_job_batches_table.php rename to database/migrations/2026_03_29_212253_create_job_batches_table.php index 5f2ec85a6f..b63999383d 100644 --- a/database/migrations/2025_12_10_154900_create_job_batches_table.php +++ b/database/migrations/2026_03_29_212253_create_job_batches_table.php @@ -8,9 +8,6 @@ return new class extends Migration { - /** - * Run the migrations. - */ public function up(): void { Schema::create('job_batches', function (Blueprint $table): void { diff --git a/database/migrations/2025_12_10_154334_create_users_table.php b/database/migrations/2026_03_29_212313_create_users_table.php similarity index 56% rename from database/migrations/2025_12_10_154334_create_users_table.php rename to database/migrations/2026_03_29_212313_create_users_table.php index ff437717f8..669fec3cac 100644 --- a/database/migrations/2025_12_10_154334_create_users_table.php +++ b/database/migrations/2026_03_29_212313_create_users_table.php @@ -8,17 +8,20 @@ return new class extends Migration { - /** - * Run the migrations. - */ public function up(): void { Schema::create('users', function (Blueprint $table): void { - $table->id(); + $table->ulid('id')->primary(); $table->string('name'); $table->string('email')->unique(); + $table->string('notification_email')->nullable(); $table->timestamp('email_verified_at')->nullable(); + $table->timestamp('notification_email_verified_at')->nullable(); $table->string('password'); + $table->rememberToken(); + $table->text('two_factor_secret')->nullable(); + $table->text('two_factor_recovery_codes')->nullable(); + $table->timestamp('two_factor_confirmed_at')->nullable(); $table->timestamps(); }); } diff --git a/database/migrations/2025_12_10_154403_create_password_reset_tokens_table.php b/database/migrations/2026_03_29_212334_create_password_reset_tokens_table.php similarity index 91% rename from database/migrations/2025_12_10_154403_create_password_reset_tokens_table.php rename to database/migrations/2026_03_29_212334_create_password_reset_tokens_table.php index 895fb8b04d..af5a726f06 100644 --- a/database/migrations/2025_12_10_154403_create_password_reset_tokens_table.php +++ b/database/migrations/2026_03_29_212334_create_password_reset_tokens_table.php @@ -8,9 +8,6 @@ return new class extends Migration { - /** - * Run the migrations. - */ public function up(): void { Schema::create('password_reset_tokens', function (Blueprint $table): void { diff --git a/rector.php b/rector.php index 97d9017bfd..5059e38bd4 100644 --- a/rector.php +++ b/rector.php @@ -3,12 +3,14 @@ declare(strict_types=1); use Rector\Caching\ValueObject\Storage\FileCacheStorage; +use Rector\CodingStyle\Rector\PostInc\PostIncDecToPreIncDecRector; use Rector\Config\RectorConfig; use Rector\Php80\Rector\NotIdentical\MbStrContainsRector; use Rector\Php83\Rector\ClassMethod\AddOverrideAttributeToOverriddenMethodsRector; use Rector\Php85\Rector\Expression\NestedFuncCallsToPipeOperatorRector; use Rector\Php85\Rector\Property\AddOverrideAttributeToOverriddenPropertiesRector; use Rector\Php85\Rector\StmtsAwareInterface\SequentialAssignmentsToPipeOperatorRector; +use RectorLaravel\Rector\Class_\AddHasFactoryToModelsRector; use RectorLaravel\Rector\Class_\RemoveModelPropertyFromFactoriesRector; use RectorLaravel\Rector\Empty_\EmptyToBlankAndFilledFuncRector; use RectorLaravel\Rector\FuncCall\ConfigToTypedConfigMethodCallRector; @@ -33,6 +35,8 @@ __DIR__.'/bootstrap/cache', AddOverrideAttributeToOverriddenMethodsRector::class, AddOverrideAttributeToOverriddenPropertiesRector::class, + PostIncDecToPreIncDecRector::class, // pint enforces post increment style via the laravel preset + AddHasFactoryToModelsRector::class, // adds HasFactory to all models also ones that do not have factories ]) ->withCache(__DIR__.'/vendor/.cache/rector', FileCacheStorage::class) ->withPhpSets()