diff --git a/.claude/skills/pest-testing/SKILL.md b/.claude/skills/pest-testing/SKILL.md
index 67455e7e6..9ca79830a 100644
--- a/.claude/skills/pest-testing/SKILL.md
+++ b/.claude/skills/pest-testing/SKILL.md
@@ -23,19 +23,28 @@ ## Documentation
Use `search-docs` for detailed Pest 4 patterns and documentation.
-## Basic Usage
+## Test Directory Structure
-### Creating Tests
+- `tests/Feature/` and `tests/Unit/` — Legacy tests (keep, don't delete)
+- `tests/v4/Feature/` — New feature tests (SQLite :memory: database)
+- `tests/v4/Browser/` — Browser tests (Pest Browser Plugin + Playwright)
+- `tests/Browser/` — Legacy Dusk browser tests (keep, don't delete)
-All tests must be written using Pest. Use `php artisan make:test --pest {name}`.
+New tests go in `tests/v4/`. The v4 suite uses SQLite :memory: with a schema dump (`database/schema/testing-schema.sql`) instead of running migrations.
-### Test Organization
+Do NOT remove tests without approval.
-- Unit/Feature tests: `tests/Feature` and `tests/Unit` directories.
-- Browser tests: `tests/Browser/` directory.
-- Do NOT remove tests without approval - these are core application code.
+## Running Tests
-### Basic Test Structure
+- All v4 tests: `php artisan test --compact tests/v4/`
+- Browser tests: `php artisan test --compact tests/v4/Browser/`
+- Feature tests: `php artisan test --compact tests/v4/Feature/`
+- Specific file: `php artisan test --compact tests/v4/Browser/LoginTest.php`
+- Filter: `php artisan test --compact --filter=testName`
+- Headed (see browser): `./vendor/bin/pest tests/v4/Browser/ --headed`
+- Debug (pause on failure): `./vendor/bin/pest tests/v4/Browser/ --debug`
+
+## Basic Test Structure
@@ -45,24 +54,10 @@ ### Basic Test Structure
-### Running Tests
-
-- Run minimal tests with filter before finalizing: `php artisan test --compact --filter=testName`.
-- Run all tests: `php artisan test --compact`.
-- Run file: `php artisan test --compact tests/Feature/ExampleTest.php`.
-
## Assertions
Use specific assertions (`assertSuccessful()`, `assertNotFound()`) instead of `assertStatus()`:
-
-
-it('returns all', function () {
- $this->postJson('/api/docs', [])->assertSuccessful();
-});
-
-
-
| Use | Instead of |
|-----|------------|
| `assertSuccessful()` | `assertStatus(200)` |
@@ -75,7 +70,7 @@ ## Mocking
## Datasets
-Use datasets for repetitive tests (validation rules, etc.):
+Use datasets for repetitive tests:
@@ -88,73 +83,94 @@ ## Datasets
-## Pest 4 Features
+## Browser Testing (Pest Browser Plugin + Playwright)
-| Feature | Purpose |
-|---------|---------|
-| Browser Testing | Full integration tests in real browsers |
-| Smoke Testing | Validate multiple pages quickly |
-| Visual Regression | Compare screenshots for visual changes |
-| Test Sharding | Parallel CI runs |
-| Architecture Testing | Enforce code conventions |
+Browser tests use `pestphp/pest-plugin-browser` with Playwright. They run **outside Docker** — the plugin starts an in-process HTTP server and Playwright browser automatically.
-### Browser Test Example
+### Key Rules
-Browser tests run in real browsers for full integration testing:
+1. **Always use `RefreshDatabase`** — the in-process server uses SQLite :memory:
+2. **Always seed `InstanceSettings::create(['id' => 0])` in `beforeEach`** — most pages crash without it
+3. **Use `User::factory()` for auth tests** — create users with `id => 0` for root user
+4. **No Dusk, no Selenium** — use `visit()`, `fill()`, `click()`, `assertSee()` from the Pest Browser API
+5. **Place tests in `tests/v4/Browser/`**
+6. **Views with bare `function` declarations** will crash on the second request in the same process — wrap with `function_exists()` guard if you encounter this
-- Browser tests live in `tests/Browser/`.
-- Use Laravel features like `Event::fake()`, `assertAuthenticated()`, and model factories.
-- Use `RefreshDatabase` for clean state per test.
-- Interact with page: click, type, scroll, select, submit, drag-and-drop, touch gestures.
-- Test on multiple browsers (Chrome, Firefox, Safari) if requested.
-- Test on different devices/viewports (iPhone 14 Pro, tablets) if requested.
-- Switch color schemes (light/dark mode) when appropriate.
-- Take screenshots or pause tests for debugging.
+### Browser Test Template
-
+
+actingAs(User::factory()->create());
+uses(RefreshDatabase::class);
- $page = visit('/sign-in');
-
- $page->assertSee('Sign In')
- ->assertNoJavaScriptErrors()
- ->click('Forgot Password?')
- ->fill('email', 'nuno@laravel.com')
- ->click('Send Reset Link')
- ->assertSee('We have emailed your password reset link!');
-
- Notification::assertSent(ResetPassword::class);
+beforeEach(function () {
+ InstanceSettings::create(['id' => 0]);
});
+it('can visit the page', function () {
+ $page = visit('/login');
+
+ $page->assertSee('Login');
+});
-### Smoke Testing
+### Browser Test with Form Interaction
-Quickly validate multiple pages have no JavaScript errors:
+
+it('fails login with invalid credentials', function () {
+ User::factory()->create([
+ 'id' => 0,
+ 'email' => 'test@example.com',
+ 'password' => Hash::make('password'),
+ ]);
-
-
-$pages = visit(['/', '/about', '/contact']);
-
-$pages->assertNoJavaScriptErrors()->assertNoConsoleLogs();
+ $page = visit('/login');
+ $page->fill('email', 'random@email.com')
+ ->fill('password', 'wrongpassword123')
+ ->click('Login')
+ ->assertSee('These credentials do not match our records');
+});
-### Visual Regression Testing
+### Browser API Reference
-Capture and compare screenshots to detect visual changes.
+| Method | Purpose |
+|--------|---------|
+| `visit('/path')` | Navigate to a page |
+| `->fill('field', 'value')` | Fill an input by name |
+| `->click('Button Text')` | Click a button/link by text |
+| `->assertSee('text')` | Assert visible text |
+| `->assertDontSee('text')` | Assert text is not visible |
+| `->assertPathIs('/path')` | Assert current URL path |
+| `->assertSeeIn('.selector', 'text')` | Assert text in element |
+| `->screenshot()` | Capture screenshot |
+| `->debug()` | Pause test, keep browser open |
+| `->wait(seconds)` | Wait N seconds |
-### Test Sharding
+### Debugging
-Split tests across parallel processes for faster CI runs.
+- Screenshots auto-saved to `tests/Browser/Screenshots/` on failure
+- `->debug()` pauses and keeps browser open (press Enter to continue)
+- `->screenshot()` captures state at any point
+- `--headed` flag shows browser, `--debug` pauses on failure
-### Architecture Testing
+## SQLite Testing Setup
-Pest 4 includes architecture testing (from Pest 3):
+v4 tests use SQLite :memory: instead of PostgreSQL. Schema loaded from `database/schema/testing-schema.sql`.
+
+### Regenerating the Schema
+
+When migrations change, regenerate from the running PostgreSQL database:
+
+```bash
+docker exec coolify php artisan schema:generate-testing
+```
+
+## Architecture Testing
@@ -171,4 +187,7 @@ ## Common Pitfalls
- Using `assertStatus(200)` instead of `assertSuccessful()`
- Forgetting datasets for repetitive validation tests
- Deleting tests without approval
-- Forgetting `assertNoJavaScriptErrors()` in browser tests
\ No newline at end of file
+- Forgetting `assertNoJavaScriptErrors()` in browser tests
+- **Browser tests: forgetting `InstanceSettings::create(['id' => 0])` — most pages crash without it**
+- **Browser tests: forgetting `RefreshDatabase` — SQLite :memory: starts empty**
+- **Browser tests: views with bare `function` declarations crash on second request — wrap with `function_exists()` guard**
diff --git a/.env.testing b/.env.testing
new file mode 100644
index 000000000..2f79f3389
--- /dev/null
+++ b/.env.testing
@@ -0,0 +1,15 @@
+APP_ENV=testing
+APP_KEY=base64:8VEfVNVkXQ9mH2L33WBWNMF4eQ0BWD5CTzB8mIxcl+k=
+APP_DEBUG=true
+
+DB_CONNECTION=testing
+
+CACHE_DRIVER=array
+SESSION_DRIVER=array
+QUEUE_CONNECTION=sync
+MAIL_MAILER=array
+TELESCOPE_ENABLED=false
+
+REDIS_HOST=127.0.0.1
+
+SELF_HOSTED=true
diff --git a/.gitignore b/.gitignore
index 935ea548e..403028761 100644
--- a/.gitignore
+++ b/.gitignore
@@ -38,3 +38,5 @@ docker/coolify-realtime/node_modules
.DS_Store
CHANGELOG.md
/.workspaces
+tests/Browser/Screenshots
+tests/v4/Browser/Screenshots
diff --git a/app/Console/Commands/GenerateTestingSchema.php b/app/Console/Commands/GenerateTestingSchema.php
new file mode 100644
index 000000000..00fd90c25
--- /dev/null
+++ b/app/Console/Commands/GenerateTestingSchema.php
@@ -0,0 +1,222 @@
+ 'INTEGER',
+ '/\binteger\b/' => 'INTEGER',
+ '/\bsmallint\b/' => 'INTEGER',
+ '/\bboolean\b/' => 'INTEGER',
+ '/character varying\(\d+\)/' => 'TEXT',
+ '/timestamp\(\d+\) without time zone/' => 'TEXT',
+ '/timestamp\(\d+\) with time zone/' => 'TEXT',
+ '/\bjsonb\b/' => 'TEXT',
+ '/\bjson\b/' => 'TEXT',
+ '/\buuid\b/' => 'TEXT',
+ '/double precision/' => 'REAL',
+ '/numeric\(\d+,\d+\)/' => 'REAL',
+ '/\bdate\b/' => 'TEXT',
+ ];
+
+ private array $castRemovals = [
+ '::character varying',
+ '::text',
+ '::integer',
+ '::boolean',
+ '::timestamp without time zone',
+ '::timestamp with time zone',
+ '::numeric',
+ ];
+
+ public function handle(): int
+ {
+ $connection = $this->option('connection');
+
+ if (DB::connection($connection)->getDriverName() !== 'pgsql') {
+ $this->error("Connection '{$connection}' is not PostgreSQL.");
+
+ return self::FAILURE;
+ }
+
+ $this->info('Reading schema from PostgreSQL...');
+
+ $tables = $this->getTables($connection);
+ $lastMigration = DB::connection($connection)
+ ->table('migrations')
+ ->orderByDesc('id')
+ ->value('migration');
+
+ $output = [];
+ $output[] = '-- Generated by: php artisan schema:generate-testing';
+ $output[] = '-- Date: '.now()->format('Y-m-d H:i:s');
+ $output[] = '-- Last migration: '.($lastMigration ?? 'none');
+ $output[] = '';
+
+ foreach ($tables as $table) {
+ $columns = $this->getColumns($connection, $table);
+ $output[] = $this->generateCreateTable($table, $columns);
+ }
+
+ $indexes = $this->getIndexes($connection, $tables);
+ foreach ($indexes as $index) {
+ $output[] = $index;
+ }
+
+ $output[] = '';
+ $output[] = '-- Migration records';
+
+ $migrations = DB::connection($connection)->table('migrations')->orderBy('id')->get();
+ foreach ($migrations as $m) {
+ $migration = str_replace("'", "''", $m->migration);
+ $output[] = "INSERT INTO \"migrations\" (\"id\", \"migration\", \"batch\") VALUES ({$m->id}, '{$migration}', {$m->batch});";
+ }
+
+ $path = database_path('schema/testing-schema.sql');
+
+ if (! is_dir(dirname($path))) {
+ mkdir(dirname($path), 0755, true);
+ }
+
+ file_put_contents($path, implode("\n", $output)."\n");
+
+ $this->info("Schema written to {$path}");
+ $this->info(count($tables).' tables, '.count($migrations).' migration records.');
+
+ return self::SUCCESS;
+ }
+
+ private function getTables(string $connection): array
+ {
+ return collect(DB::connection($connection)->select(
+ "SELECT tablename FROM pg_tables WHERE schemaname = 'public' ORDER BY tablename"
+ ))->pluck('tablename')->toArray();
+ }
+
+ private function getColumns(string $connection, string $table): array
+ {
+ return DB::connection($connection)->select(
+ "SELECT column_name, data_type, character_maximum_length, column_default,
+ is_nullable, udt_name, numeric_precision, numeric_scale, datetime_precision
+ FROM information_schema.columns
+ WHERE table_schema = 'public' AND table_name = ?
+ ORDER BY ordinal_position",
+ [$table]
+ );
+ }
+
+ private function generateCreateTable(string $table, array $columns): string
+ {
+ $lines = [];
+
+ foreach ($columns as $col) {
+ $lines[] = ' '.$this->generateColumnDef($table, $col);
+ }
+
+ return "CREATE TABLE IF NOT EXISTS \"{$table}\" (\n".implode(",\n", $lines)."\n);\n";
+ }
+
+ private function generateColumnDef(string $table, object $col): string
+ {
+ $name = $col->column_name;
+ $sqliteType = $this->convertType($col);
+
+ // Auto-increment primary key for id columns
+ if ($name === 'id' && $sqliteType === 'INTEGER' && $col->is_nullable === 'NO' && str_contains((string) $col->column_default, 'nextval')) {
+ return "\"{$name}\" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL";
+ }
+
+ $parts = ["\"{$name}\"", $sqliteType];
+
+ // Default value
+ $default = $col->column_default;
+ if ($default !== null && ! str_contains($default, 'nextval')) {
+ $default = $this->cleanDefault($default);
+ $parts[] = "DEFAULT {$default}";
+ }
+
+ // NOT NULL
+ if ($col->is_nullable === 'NO') {
+ $parts[] = 'NOT NULL';
+ }
+
+ return implode(' ', $parts);
+ }
+
+ private function convertType(object $col): string
+ {
+ $pgType = $col->data_type;
+
+ return match (true) {
+ in_array($pgType, ['bigint', 'integer', 'smallint']) => 'INTEGER',
+ $pgType === 'boolean' => 'INTEGER',
+ in_array($pgType, ['character varying', 'text', 'USER-DEFINED']) => 'TEXT',
+ str_contains($pgType, 'timestamp') => 'TEXT',
+ in_array($pgType, ['json', 'jsonb']) => 'TEXT',
+ $pgType === 'uuid' => 'TEXT',
+ $pgType === 'double precision' => 'REAL',
+ $pgType === 'numeric' => 'REAL',
+ $pgType === 'date' => 'TEXT',
+ default => 'TEXT',
+ };
+ }
+
+ private function cleanDefault(string $default): string
+ {
+ foreach ($this->castRemovals as $cast) {
+ $default = str_replace($cast, '', $default);
+ }
+
+ // Remove array type casts like ::text[]
+ $default = preg_replace('/::[\w\s]+(\[\])?/', '', $default);
+
+ return $default;
+ }
+
+ private function getIndexes(string $connection, array $tables): array
+ {
+ $results = [];
+
+ $indexes = DB::connection($connection)->select(
+ "SELECT indexname, tablename, indexdef FROM pg_indexes
+ WHERE schemaname = 'public'
+ ORDER BY tablename, indexname"
+ );
+
+ foreach ($indexes as $idx) {
+ $def = $idx->indexdef;
+
+ // Skip primary key indexes
+ if (str_contains($def, '_pkey')) {
+ continue;
+ }
+
+ // Skip PG-specific indexes (GIN, GIST, expression indexes)
+ if (preg_match('/USING (gin|gist)/i', $def)) {
+ continue;
+ }
+ if (str_contains($def, '->>') || str_contains($def, '::')) {
+ continue;
+ }
+
+ // Convert to SQLite-compatible CREATE INDEX
+ $unique = str_contains($def, 'UNIQUE') ? 'UNIQUE ' : '';
+
+ // Extract columns from the index definition
+ if (preg_match('/\((.+)\)$/', $def, $m)) {
+ $cols = $m[1];
+ $results[] = "CREATE {$unique}INDEX IF NOT EXISTS \"{$idx->indexname}\" ON \"{$idx->tablename}\" ({$cols});";
+ }
+ }
+
+ return $results;
+ }
+}
diff --git a/bootstrap/helpers/shared.php b/bootstrap/helpers/shared.php
index 2173e7619..4372ff955 100644
--- a/bootstrap/helpers/shared.php
+++ b/bootstrap/helpers/shared.php
@@ -167,6 +167,10 @@ function currentTeam()
function showBoarding(): bool
{
+ if (isDev()) {
+ return false;
+ }
+
if (Auth::user()?->isMember()) {
return false;
}
diff --git a/composer.json b/composer.json
index 1c36df1ea..fc71dea8f 100644
--- a/composer.json
+++ b/composer.json
@@ -69,6 +69,7 @@
"mockery/mockery": "^1.6.12",
"nunomaduro/collision": "^8.8.3",
"pestphp/pest": "^4.3.2",
+ "pestphp/pest-plugin-browser": "^4.2",
"phpstan/phpstan": "^2.1.38",
"rector/rector": "^2.3.5",
"serversideup/spin": "^3.1.1",
diff --git a/composer.lock b/composer.lock
index 708382500..7c1e000e5 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "d22beb5f7db243339d029a288bdfe33e",
+ "content-hash": "21d43f41d2f2e275403e77ccc66ec553",
"packages": [
{
"name": "aws/aws-crt-php",
@@ -11636,6 +11636,1228 @@
}
],
"packages-dev": [
+ {
+ "name": "amphp/amp",
+ "version": "v3.1.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/amphp/amp.git",
+ "reference": "fa0ab33a6f47a82929c38d03ca47ebb71086a93f"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/amphp/amp/zipball/fa0ab33a6f47a82929c38d03ca47ebb71086a93f",
+ "reference": "fa0ab33a6f47a82929c38d03ca47ebb71086a93f",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1",
+ "revolt/event-loop": "^1 || ^0.2"
+ },
+ "require-dev": {
+ "amphp/php-cs-fixer-config": "^2",
+ "phpunit/phpunit": "^9",
+ "psalm/phar": "5.23.1"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "src/functions.php",
+ "src/Future/functions.php",
+ "src/Internal/functions.php"
+ ],
+ "psr-4": {
+ "Amp\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Aaron Piotrowski",
+ "email": "aaron@trowski.com"
+ },
+ {
+ "name": "Bob Weinand",
+ "email": "bobwei9@hotmail.com"
+ },
+ {
+ "name": "Niklas Keller",
+ "email": "me@kelunik.com"
+ },
+ {
+ "name": "Daniel Lowrey",
+ "email": "rdlowrey@php.net"
+ }
+ ],
+ "description": "A non-blocking concurrency framework for PHP applications.",
+ "homepage": "https://amphp.org/amp",
+ "keywords": [
+ "async",
+ "asynchronous",
+ "awaitable",
+ "concurrency",
+ "event",
+ "event-loop",
+ "future",
+ "non-blocking",
+ "promise"
+ ],
+ "support": {
+ "issues": "https://github.com/amphp/amp/issues",
+ "source": "https://github.com/amphp/amp/tree/v3.1.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/amphp",
+ "type": "github"
+ }
+ ],
+ "time": "2025-08-27T21:42:00+00:00"
+ },
+ {
+ "name": "amphp/byte-stream",
+ "version": "v2.1.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/amphp/byte-stream.git",
+ "reference": "55a6bd071aec26fa2a3e002618c20c35e3df1b46"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/amphp/byte-stream/zipball/55a6bd071aec26fa2a3e002618c20c35e3df1b46",
+ "reference": "55a6bd071aec26fa2a3e002618c20c35e3df1b46",
+ "shasum": ""
+ },
+ "require": {
+ "amphp/amp": "^3",
+ "amphp/parser": "^1.1",
+ "amphp/pipeline": "^1",
+ "amphp/serialization": "^1",
+ "amphp/sync": "^2",
+ "php": ">=8.1",
+ "revolt/event-loop": "^1 || ^0.2.3"
+ },
+ "require-dev": {
+ "amphp/php-cs-fixer-config": "^2",
+ "amphp/phpunit-util": "^3",
+ "phpunit/phpunit": "^9",
+ "psalm/phar": "5.22.1"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "src/functions.php",
+ "src/Internal/functions.php"
+ ],
+ "psr-4": {
+ "Amp\\ByteStream\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Aaron Piotrowski",
+ "email": "aaron@trowski.com"
+ },
+ {
+ "name": "Niklas Keller",
+ "email": "me@kelunik.com"
+ }
+ ],
+ "description": "A stream abstraction to make working with non-blocking I/O simple.",
+ "homepage": "https://amphp.org/byte-stream",
+ "keywords": [
+ "amp",
+ "amphp",
+ "async",
+ "io",
+ "non-blocking",
+ "stream"
+ ],
+ "support": {
+ "issues": "https://github.com/amphp/byte-stream/issues",
+ "source": "https://github.com/amphp/byte-stream/tree/v2.1.2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/amphp",
+ "type": "github"
+ }
+ ],
+ "time": "2025-03-16T17:10:27+00:00"
+ },
+ {
+ "name": "amphp/cache",
+ "version": "v2.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/amphp/cache.git",
+ "reference": "46912e387e6aa94933b61ea1ead9cf7540b7797c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/amphp/cache/zipball/46912e387e6aa94933b61ea1ead9cf7540b7797c",
+ "reference": "46912e387e6aa94933b61ea1ead9cf7540b7797c",
+ "shasum": ""
+ },
+ "require": {
+ "amphp/amp": "^3",
+ "amphp/serialization": "^1",
+ "amphp/sync": "^2",
+ "php": ">=8.1",
+ "revolt/event-loop": "^1 || ^0.2"
+ },
+ "require-dev": {
+ "amphp/php-cs-fixer-config": "^2",
+ "amphp/phpunit-util": "^3",
+ "phpunit/phpunit": "^9",
+ "psalm/phar": "^5.4"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Amp\\Cache\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Niklas Keller",
+ "email": "me@kelunik.com"
+ },
+ {
+ "name": "Aaron Piotrowski",
+ "email": "aaron@trowski.com"
+ },
+ {
+ "name": "Daniel Lowrey",
+ "email": "rdlowrey@php.net"
+ }
+ ],
+ "description": "A fiber-aware cache API based on Amp and Revolt.",
+ "homepage": "https://amphp.org/cache",
+ "support": {
+ "issues": "https://github.com/amphp/cache/issues",
+ "source": "https://github.com/amphp/cache/tree/v2.0.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/amphp",
+ "type": "github"
+ }
+ ],
+ "time": "2024-04-19T03:38:06+00:00"
+ },
+ {
+ "name": "amphp/dns",
+ "version": "v2.4.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/amphp/dns.git",
+ "reference": "78eb3db5fc69bf2fc0cb503c4fcba667bc223c71"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/amphp/dns/zipball/78eb3db5fc69bf2fc0cb503c4fcba667bc223c71",
+ "reference": "78eb3db5fc69bf2fc0cb503c4fcba667bc223c71",
+ "shasum": ""
+ },
+ "require": {
+ "amphp/amp": "^3",
+ "amphp/byte-stream": "^2",
+ "amphp/cache": "^2",
+ "amphp/parser": "^1",
+ "amphp/process": "^2",
+ "daverandom/libdns": "^2.0.2",
+ "ext-filter": "*",
+ "ext-json": "*",
+ "php": ">=8.1",
+ "revolt/event-loop": "^1 || ^0.2"
+ },
+ "require-dev": {
+ "amphp/php-cs-fixer-config": "^2",
+ "amphp/phpunit-util": "^3",
+ "phpunit/phpunit": "^9",
+ "psalm/phar": "5.20"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "src/functions.php"
+ ],
+ "psr-4": {
+ "Amp\\Dns\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Chris Wright",
+ "email": "addr@daverandom.com"
+ },
+ {
+ "name": "Daniel Lowrey",
+ "email": "rdlowrey@php.net"
+ },
+ {
+ "name": "Bob Weinand",
+ "email": "bobwei9@hotmail.com"
+ },
+ {
+ "name": "Niklas Keller",
+ "email": "me@kelunik.com"
+ },
+ {
+ "name": "Aaron Piotrowski",
+ "email": "aaron@trowski.com"
+ }
+ ],
+ "description": "Async DNS resolution for Amp.",
+ "homepage": "https://github.com/amphp/dns",
+ "keywords": [
+ "amp",
+ "amphp",
+ "async",
+ "client",
+ "dns",
+ "resolve"
+ ],
+ "support": {
+ "issues": "https://github.com/amphp/dns/issues",
+ "source": "https://github.com/amphp/dns/tree/v2.4.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/amphp",
+ "type": "github"
+ }
+ ],
+ "time": "2025-01-19T15:43:40+00:00"
+ },
+ {
+ "name": "amphp/hpack",
+ "version": "v3.2.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/amphp/hpack.git",
+ "reference": "4f293064b15682a2b178b1367ddf0b8b5feb0239"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/amphp/hpack/zipball/4f293064b15682a2b178b1367ddf0b8b5feb0239",
+ "reference": "4f293064b15682a2b178b1367ddf0b8b5feb0239",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1"
+ },
+ "require-dev": {
+ "amphp/php-cs-fixer-config": "^2",
+ "http2jp/hpack-test-case": "^1",
+ "nikic/php-fuzzer": "^0.0.10",
+ "phpunit/phpunit": "^7 | ^8 | ^9"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Amp\\Http\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Daniel Lowrey",
+ "email": "rdlowrey@php.net"
+ },
+ {
+ "name": "Bob Weinand"
+ },
+ {
+ "name": "Niklas Keller",
+ "email": "me@kelunik.com"
+ },
+ {
+ "name": "Aaron Piotrowski",
+ "email": "aaron@trowski.com"
+ }
+ ],
+ "description": "HTTP/2 HPack implementation.",
+ "homepage": "https://github.com/amphp/hpack",
+ "keywords": [
+ "headers",
+ "hpack",
+ "http-2"
+ ],
+ "support": {
+ "issues": "https://github.com/amphp/hpack/issues",
+ "source": "https://github.com/amphp/hpack/tree/v3.2.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/amphp",
+ "type": "github"
+ }
+ ],
+ "time": "2024-03-21T19:00:16+00:00"
+ },
+ {
+ "name": "amphp/http",
+ "version": "v2.1.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/amphp/http.git",
+ "reference": "3680d80bd38b5d6f3c2cef2214ca6dd6cef26588"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/amphp/http/zipball/3680d80bd38b5d6f3c2cef2214ca6dd6cef26588",
+ "reference": "3680d80bd38b5d6f3c2cef2214ca6dd6cef26588",
+ "shasum": ""
+ },
+ "require": {
+ "amphp/hpack": "^3",
+ "amphp/parser": "^1.1",
+ "league/uri-components": "^2.4.2 | ^7.1",
+ "php": ">=8.1",
+ "psr/http-message": "^1 | ^2"
+ },
+ "require-dev": {
+ "amphp/php-cs-fixer-config": "^2",
+ "league/uri": "^6.8 | ^7.1",
+ "phpunit/phpunit": "^9",
+ "psalm/phar": "^5.26.1"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "src/functions.php",
+ "src/Internal/constants.php"
+ ],
+ "psr-4": {
+ "Amp\\Http\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Niklas Keller",
+ "email": "me@kelunik.com"
+ },
+ {
+ "name": "Aaron Piotrowski",
+ "email": "aaron@trowski.com"
+ }
+ ],
+ "description": "Basic HTTP primitives which can be shared by servers and clients.",
+ "support": {
+ "issues": "https://github.com/amphp/http/issues",
+ "source": "https://github.com/amphp/http/tree/v2.1.2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/amphp",
+ "type": "github"
+ }
+ ],
+ "time": "2024-11-23T14:57:26+00:00"
+ },
+ {
+ "name": "amphp/http-client",
+ "version": "v5.3.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/amphp/http-client.git",
+ "reference": "75ad21574fd632594a2dd914496647816d5106bc"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/amphp/http-client/zipball/75ad21574fd632594a2dd914496647816d5106bc",
+ "reference": "75ad21574fd632594a2dd914496647816d5106bc",
+ "shasum": ""
+ },
+ "require": {
+ "amphp/amp": "^3",
+ "amphp/byte-stream": "^2",
+ "amphp/hpack": "^3",
+ "amphp/http": "^2",
+ "amphp/pipeline": "^1",
+ "amphp/socket": "^2",
+ "amphp/sync": "^2",
+ "league/uri": "^7",
+ "league/uri-components": "^7",
+ "league/uri-interfaces": "^7.1",
+ "php": ">=8.1",
+ "psr/http-message": "^1 | ^2",
+ "revolt/event-loop": "^1"
+ },
+ "conflict": {
+ "amphp/file": "<3 | >=5"
+ },
+ "require-dev": {
+ "amphp/file": "^3 | ^4",
+ "amphp/http-server": "^3",
+ "amphp/php-cs-fixer-config": "^2",
+ "amphp/phpunit-util": "^3",
+ "ext-json": "*",
+ "kelunik/link-header-rfc5988": "^1",
+ "laminas/laminas-diactoros": "^2.3",
+ "phpunit/phpunit": "^9",
+ "psalm/phar": "~5.23"
+ },
+ "suggest": {
+ "amphp/file": "Required for file request bodies and HTTP archive logging",
+ "ext-json": "Required for logging HTTP archives",
+ "ext-zlib": "Allows using compression for response bodies."
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "src/functions.php",
+ "src/Internal/functions.php"
+ ],
+ "psr-4": {
+ "Amp\\Http\\Client\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Daniel Lowrey",
+ "email": "rdlowrey@gmail.com"
+ },
+ {
+ "name": "Niklas Keller",
+ "email": "me@kelunik.com"
+ },
+ {
+ "name": "Aaron Piotrowski",
+ "email": "aaron@trowski.com"
+ }
+ ],
+ "description": "An advanced async HTTP client library for PHP, enabling efficient, non-blocking, and concurrent requests and responses.",
+ "homepage": "https://amphp.org/http-client",
+ "keywords": [
+ "async",
+ "client",
+ "concurrent",
+ "http",
+ "non-blocking",
+ "rest"
+ ],
+ "support": {
+ "issues": "https://github.com/amphp/http-client/issues",
+ "source": "https://github.com/amphp/http-client/tree/v5.3.4"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/amphp",
+ "type": "github"
+ }
+ ],
+ "time": "2025-08-16T20:41:23+00:00"
+ },
+ {
+ "name": "amphp/http-server",
+ "version": "v3.4.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/amphp/http-server.git",
+ "reference": "8dc32cc6a65c12a3543276305796b993c56b76ef"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/amphp/http-server/zipball/8dc32cc6a65c12a3543276305796b993c56b76ef",
+ "reference": "8dc32cc6a65c12a3543276305796b993c56b76ef",
+ "shasum": ""
+ },
+ "require": {
+ "amphp/amp": "^3",
+ "amphp/byte-stream": "^2",
+ "amphp/cache": "^2",
+ "amphp/hpack": "^3",
+ "amphp/http": "^2",
+ "amphp/pipeline": "^1",
+ "amphp/socket": "^2.1",
+ "amphp/sync": "^2.2",
+ "league/uri": "^7.1",
+ "league/uri-interfaces": "^7.1",
+ "php": ">=8.1",
+ "psr/http-message": "^1 | ^2",
+ "psr/log": "^1 | ^2 | ^3",
+ "revolt/event-loop": "^1"
+ },
+ "require-dev": {
+ "amphp/http-client": "^5",
+ "amphp/log": "^2",
+ "amphp/php-cs-fixer-config": "^2",
+ "amphp/phpunit-util": "^3",
+ "league/uri-components": "^7.1",
+ "monolog/monolog": "^3",
+ "phpunit/phpunit": "^9",
+ "psalm/phar": "~5.23"
+ },
+ "suggest": {
+ "ext-zlib": "Allows GZip compression of response bodies"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "src/Driver/functions.php",
+ "src/Middleware/functions.php",
+ "src/functions.php"
+ ],
+ "psr-4": {
+ "Amp\\Http\\Server\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Daniel Lowrey",
+ "email": "rdlowrey@php.net"
+ },
+ {
+ "name": "Bob Weinand"
+ },
+ {
+ "name": "Niklas Keller",
+ "email": "me@kelunik.com"
+ },
+ {
+ "name": "Aaron Piotrowski",
+ "email": "aaron@trowski.com"
+ }
+ ],
+ "description": "A non-blocking HTTP application server for PHP based on Amp.",
+ "homepage": "https://github.com/amphp/http-server",
+ "keywords": [
+ "amp",
+ "amphp",
+ "async",
+ "http",
+ "non-blocking",
+ "server"
+ ],
+ "support": {
+ "issues": "https://github.com/amphp/http-server/issues",
+ "source": "https://github.com/amphp/http-server/tree/v3.4.4"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/amphp",
+ "type": "github"
+ }
+ ],
+ "time": "2026-02-08T18:16:29+00:00"
+ },
+ {
+ "name": "amphp/parser",
+ "version": "v1.1.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/amphp/parser.git",
+ "reference": "3cf1f8b32a0171d4b1bed93d25617637a77cded7"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/amphp/parser/zipball/3cf1f8b32a0171d4b1bed93d25617637a77cded7",
+ "reference": "3cf1f8b32a0171d4b1bed93d25617637a77cded7",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.4"
+ },
+ "require-dev": {
+ "amphp/php-cs-fixer-config": "^2",
+ "phpunit/phpunit": "^9",
+ "psalm/phar": "^5.4"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Amp\\Parser\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Aaron Piotrowski",
+ "email": "aaron@trowski.com"
+ },
+ {
+ "name": "Niklas Keller",
+ "email": "me@kelunik.com"
+ }
+ ],
+ "description": "A generator parser to make streaming parsers simple.",
+ "homepage": "https://github.com/amphp/parser",
+ "keywords": [
+ "async",
+ "non-blocking",
+ "parser",
+ "stream"
+ ],
+ "support": {
+ "issues": "https://github.com/amphp/parser/issues",
+ "source": "https://github.com/amphp/parser/tree/v1.1.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/amphp",
+ "type": "github"
+ }
+ ],
+ "time": "2024-03-21T19:16:53+00:00"
+ },
+ {
+ "name": "amphp/pipeline",
+ "version": "v1.2.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/amphp/pipeline.git",
+ "reference": "7b52598c2e9105ebcddf247fc523161581930367"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/amphp/pipeline/zipball/7b52598c2e9105ebcddf247fc523161581930367",
+ "reference": "7b52598c2e9105ebcddf247fc523161581930367",
+ "shasum": ""
+ },
+ "require": {
+ "amphp/amp": "^3",
+ "php": ">=8.1",
+ "revolt/event-loop": "^1"
+ },
+ "require-dev": {
+ "amphp/php-cs-fixer-config": "^2",
+ "amphp/phpunit-util": "^3",
+ "phpunit/phpunit": "^9",
+ "psalm/phar": "^5.18"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Amp\\Pipeline\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Aaron Piotrowski",
+ "email": "aaron@trowski.com"
+ },
+ {
+ "name": "Niklas Keller",
+ "email": "me@kelunik.com"
+ }
+ ],
+ "description": "Asynchronous iterators and operators.",
+ "homepage": "https://amphp.org/pipeline",
+ "keywords": [
+ "amp",
+ "amphp",
+ "async",
+ "io",
+ "iterator",
+ "non-blocking"
+ ],
+ "support": {
+ "issues": "https://github.com/amphp/pipeline/issues",
+ "source": "https://github.com/amphp/pipeline/tree/v1.2.3"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/amphp",
+ "type": "github"
+ }
+ ],
+ "time": "2025-03-16T16:33:53+00:00"
+ },
+ {
+ "name": "amphp/process",
+ "version": "v2.0.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/amphp/process.git",
+ "reference": "52e08c09dec7511d5fbc1fb00d3e4e79fc77d58d"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/amphp/process/zipball/52e08c09dec7511d5fbc1fb00d3e4e79fc77d58d",
+ "reference": "52e08c09dec7511d5fbc1fb00d3e4e79fc77d58d",
+ "shasum": ""
+ },
+ "require": {
+ "amphp/amp": "^3",
+ "amphp/byte-stream": "^2",
+ "amphp/sync": "^2",
+ "php": ">=8.1",
+ "revolt/event-loop": "^1 || ^0.2"
+ },
+ "require-dev": {
+ "amphp/php-cs-fixer-config": "^2",
+ "amphp/phpunit-util": "^3",
+ "phpunit/phpunit": "^9",
+ "psalm/phar": "^5.4"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "src/functions.php"
+ ],
+ "psr-4": {
+ "Amp\\Process\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Bob Weinand",
+ "email": "bobwei9@hotmail.com"
+ },
+ {
+ "name": "Aaron Piotrowski",
+ "email": "aaron@trowski.com"
+ },
+ {
+ "name": "Niklas Keller",
+ "email": "me@kelunik.com"
+ }
+ ],
+ "description": "A fiber-aware process manager based on Amp and Revolt.",
+ "homepage": "https://amphp.org/process",
+ "support": {
+ "issues": "https://github.com/amphp/process/issues",
+ "source": "https://github.com/amphp/process/tree/v2.0.3"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/amphp",
+ "type": "github"
+ }
+ ],
+ "time": "2024-04-19T03:13:44+00:00"
+ },
+ {
+ "name": "amphp/serialization",
+ "version": "v1.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/amphp/serialization.git",
+ "reference": "693e77b2fb0b266c3c7d622317f881de44ae94a1"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/amphp/serialization/zipball/693e77b2fb0b266c3c7d622317f881de44ae94a1",
+ "reference": "693e77b2fb0b266c3c7d622317f881de44ae94a1",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1"
+ },
+ "require-dev": {
+ "amphp/php-cs-fixer-config": "dev-master",
+ "phpunit/phpunit": "^9 || ^8 || ^7"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "src/functions.php"
+ ],
+ "psr-4": {
+ "Amp\\Serialization\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Aaron Piotrowski",
+ "email": "aaron@trowski.com"
+ },
+ {
+ "name": "Niklas Keller",
+ "email": "me@kelunik.com"
+ }
+ ],
+ "description": "Serialization tools for IPC and data storage in PHP.",
+ "homepage": "https://github.com/amphp/serialization",
+ "keywords": [
+ "async",
+ "asynchronous",
+ "serialization",
+ "serialize"
+ ],
+ "support": {
+ "issues": "https://github.com/amphp/serialization/issues",
+ "source": "https://github.com/amphp/serialization/tree/master"
+ },
+ "time": "2020-03-25T21:39:07+00:00"
+ },
+ {
+ "name": "amphp/socket",
+ "version": "v2.3.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/amphp/socket.git",
+ "reference": "58e0422221825b79681b72c50c47a930be7bf1e1"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/amphp/socket/zipball/58e0422221825b79681b72c50c47a930be7bf1e1",
+ "reference": "58e0422221825b79681b72c50c47a930be7bf1e1",
+ "shasum": ""
+ },
+ "require": {
+ "amphp/amp": "^3",
+ "amphp/byte-stream": "^2",
+ "amphp/dns": "^2",
+ "ext-openssl": "*",
+ "kelunik/certificate": "^1.1",
+ "league/uri": "^6.5 | ^7",
+ "league/uri-interfaces": "^2.3 | ^7",
+ "php": ">=8.1",
+ "revolt/event-loop": "^1 || ^0.2"
+ },
+ "require-dev": {
+ "amphp/php-cs-fixer-config": "^2",
+ "amphp/phpunit-util": "^3",
+ "amphp/process": "^2",
+ "phpunit/phpunit": "^9",
+ "psalm/phar": "5.20"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "src/functions.php",
+ "src/Internal/functions.php",
+ "src/SocketAddress/functions.php"
+ ],
+ "psr-4": {
+ "Amp\\Socket\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Daniel Lowrey",
+ "email": "rdlowrey@gmail.com"
+ },
+ {
+ "name": "Aaron Piotrowski",
+ "email": "aaron@trowski.com"
+ },
+ {
+ "name": "Niklas Keller",
+ "email": "me@kelunik.com"
+ }
+ ],
+ "description": "Non-blocking socket connection / server implementations based on Amp and Revolt.",
+ "homepage": "https://github.com/amphp/socket",
+ "keywords": [
+ "amp",
+ "async",
+ "encryption",
+ "non-blocking",
+ "sockets",
+ "tcp",
+ "tls"
+ ],
+ "support": {
+ "issues": "https://github.com/amphp/socket/issues",
+ "source": "https://github.com/amphp/socket/tree/v2.3.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/amphp",
+ "type": "github"
+ }
+ ],
+ "time": "2024-04-21T14:33:03+00:00"
+ },
+ {
+ "name": "amphp/sync",
+ "version": "v2.3.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/amphp/sync.git",
+ "reference": "217097b785130d77cfcc58ff583cf26cd1770bf1"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/amphp/sync/zipball/217097b785130d77cfcc58ff583cf26cd1770bf1",
+ "reference": "217097b785130d77cfcc58ff583cf26cd1770bf1",
+ "shasum": ""
+ },
+ "require": {
+ "amphp/amp": "^3",
+ "amphp/pipeline": "^1",
+ "amphp/serialization": "^1",
+ "php": ">=8.1",
+ "revolt/event-loop": "^1 || ^0.2"
+ },
+ "require-dev": {
+ "amphp/php-cs-fixer-config": "^2",
+ "amphp/phpunit-util": "^3",
+ "phpunit/phpunit": "^9",
+ "psalm/phar": "5.23"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "src/functions.php"
+ ],
+ "psr-4": {
+ "Amp\\Sync\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Aaron Piotrowski",
+ "email": "aaron@trowski.com"
+ },
+ {
+ "name": "Niklas Keller",
+ "email": "me@kelunik.com"
+ },
+ {
+ "name": "Stephen Coakley",
+ "email": "me@stephencoakley.com"
+ }
+ ],
+ "description": "Non-blocking synchronization primitives for PHP based on Amp and Revolt.",
+ "homepage": "https://github.com/amphp/sync",
+ "keywords": [
+ "async",
+ "asynchronous",
+ "mutex",
+ "semaphore",
+ "synchronization"
+ ],
+ "support": {
+ "issues": "https://github.com/amphp/sync/issues",
+ "source": "https://github.com/amphp/sync/tree/v2.3.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/amphp",
+ "type": "github"
+ }
+ ],
+ "time": "2024-08-03T19:31:26+00:00"
+ },
+ {
+ "name": "amphp/websocket",
+ "version": "v2.0.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/amphp/websocket.git",
+ "reference": "963904b6a883c4b62d9222d1d9749814fac96a3b"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/amphp/websocket/zipball/963904b6a883c4b62d9222d1d9749814fac96a3b",
+ "reference": "963904b6a883c4b62d9222d1d9749814fac96a3b",
+ "shasum": ""
+ },
+ "require": {
+ "amphp/amp": "^3",
+ "amphp/byte-stream": "^2",
+ "amphp/parser": "^1",
+ "amphp/pipeline": "^1",
+ "amphp/socket": "^2",
+ "php": ">=8.1",
+ "revolt/event-loop": "^1"
+ },
+ "require-dev": {
+ "amphp/php-cs-fixer-config": "^2",
+ "amphp/phpunit-util": "^3",
+ "phpunit/phpunit": "^9",
+ "psalm/phar": "^5.18"
+ },
+ "suggest": {
+ "ext-zlib": "Required for compression"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "src/functions.php"
+ ],
+ "psr-4": {
+ "Amp\\Websocket\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Aaron Piotrowski",
+ "email": "aaron@trowski.com"
+ },
+ {
+ "name": "Niklas Keller",
+ "email": "me@kelunik.com"
+ },
+ {
+ "name": "Bob Weinand",
+ "email": "bobwei9@hotmail.com"
+ }
+ ],
+ "description": "Shared code for websocket servers and clients.",
+ "homepage": "https://github.com/amphp/websocket",
+ "keywords": [
+ "amp",
+ "amphp",
+ "async",
+ "http",
+ "non-blocking",
+ "websocket"
+ ],
+ "support": {
+ "issues": "https://github.com/amphp/websocket/issues",
+ "source": "https://github.com/amphp/websocket/tree/v2.0.4"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/amphp",
+ "type": "github"
+ }
+ ],
+ "time": "2024-10-28T21:28:45+00:00"
+ },
+ {
+ "name": "amphp/websocket-client",
+ "version": "v2.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/amphp/websocket-client.git",
+ "reference": "dc033fdce0af56295a23f63ac4f579b34d470d6c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/amphp/websocket-client/zipball/dc033fdce0af56295a23f63ac4f579b34d470d6c",
+ "reference": "dc033fdce0af56295a23f63ac4f579b34d470d6c",
+ "shasum": ""
+ },
+ "require": {
+ "amphp/amp": "^3",
+ "amphp/byte-stream": "^2.1",
+ "amphp/http": "^2.1",
+ "amphp/http-client": "^5",
+ "amphp/socket": "^2.2",
+ "amphp/websocket": "^2",
+ "league/uri": "^7.1",
+ "php": ">=8.1",
+ "psr/http-message": "^1|^2",
+ "revolt/event-loop": "^1"
+ },
+ "require-dev": {
+ "amphp/http-server": "^3",
+ "amphp/php-cs-fixer-config": "^2",
+ "amphp/phpunit-util": "^3",
+ "amphp/websocket-server": "^3|^4",
+ "phpunit/phpunit": "^9",
+ "psalm/phar": "~5.26.1",
+ "psr/log": "^1"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "src/functions.php"
+ ],
+ "psr-4": {
+ "Amp\\Websocket\\Client\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Bob Weinand",
+ "email": "bobwei9@hotmail.com"
+ },
+ {
+ "name": "Aaron Piotrowski",
+ "email": "aaron@trowski.com"
+ },
+ {
+ "name": "Niklas Keller",
+ "email": "me@kelunik.com"
+ }
+ ],
+ "description": "Async WebSocket client for PHP based on Amp.",
+ "keywords": [
+ "amp",
+ "amphp",
+ "async",
+ "client",
+ "http",
+ "non-blocking",
+ "websocket"
+ ],
+ "support": {
+ "issues": "https://github.com/amphp/websocket-client/issues",
+ "source": "https://github.com/amphp/websocket-client/tree/v2.0.2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/amphp",
+ "type": "github"
+ }
+ ],
+ "time": "2025-08-24T17:25:34+00:00"
+ },
{
"name": "barryvdh/laravel-debugbar",
"version": "v3.16.5",
@@ -11814,6 +13036,50 @@
],
"time": "2026-01-08T07:23:06+00:00"
},
+ {
+ "name": "daverandom/libdns",
+ "version": "v2.1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/DaveRandom/LibDNS.git",
+ "reference": "b84c94e8fe6b7ee4aecfe121bfe3b6177d303c8a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/DaveRandom/LibDNS/zipball/b84c94e8fe6b7ee4aecfe121bfe3b6177d303c8a",
+ "reference": "b84c94e8fe6b7ee4aecfe121bfe3b6177d303c8a",
+ "shasum": ""
+ },
+ "require": {
+ "ext-ctype": "*",
+ "php": ">=7.1"
+ },
+ "suggest": {
+ "ext-intl": "Required for IDN support"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "src/functions.php"
+ ],
+ "psr-4": {
+ "LibDNS\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "DNS protocol implementation written in pure PHP",
+ "keywords": [
+ "dns"
+ ],
+ "support": {
+ "issues": "https://github.com/DaveRandom/LibDNS/issues",
+ "source": "https://github.com/DaveRandom/LibDNS/tree/v2.1.0"
+ },
+ "time": "2024-04-12T12:12:48+00:00"
+ },
{
"name": "driftingly/rector-laravel",
"version": "2.1.9",
@@ -12096,6 +13362,64 @@
},
"time": "2025-04-30T06:54:44+00:00"
},
+ {
+ "name": "kelunik/certificate",
+ "version": "v1.1.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/kelunik/certificate.git",
+ "reference": "7e00d498c264d5eb4f78c69f41c8bd6719c0199e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/kelunik/certificate/zipball/7e00d498c264d5eb4f78c69f41c8bd6719c0199e",
+ "reference": "7e00d498c264d5eb4f78c69f41c8bd6719c0199e",
+ "shasum": ""
+ },
+ "require": {
+ "ext-openssl": "*",
+ "php": ">=7.0"
+ },
+ "require-dev": {
+ "amphp/php-cs-fixer-config": "^2",
+ "phpunit/phpunit": "^6 | 7 | ^8 | ^9"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Kelunik\\Certificate\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Niklas Keller",
+ "email": "me@kelunik.com"
+ }
+ ],
+ "description": "Access certificate details and transform between different formats.",
+ "keywords": [
+ "DER",
+ "certificate",
+ "certificates",
+ "openssl",
+ "pem",
+ "x509"
+ ],
+ "support": {
+ "issues": "https://github.com/kelunik/certificate/issues",
+ "source": "https://github.com/kelunik/certificate/tree/v1.1.3"
+ },
+ "time": "2023-02-03T21:26:53+00:00"
+ },
{
"name": "laravel/boost",
"version": "v2.1.1",
@@ -12505,6 +13829,90 @@
},
"time": "2025-12-30T17:31:31+00:00"
},
+ {
+ "name": "league/uri-components",
+ "version": "7.8.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/thephpleague/uri-components.git",
+ "reference": "8b5ffcebcc0842b76eb80964795bd56a8333b2ba"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/thephpleague/uri-components/zipball/8b5ffcebcc0842b76eb80964795bd56a8333b2ba",
+ "reference": "8b5ffcebcc0842b76eb80964795bd56a8333b2ba",
+ "shasum": ""
+ },
+ "require": {
+ "league/uri": "^7.8",
+ "php": "^8.1"
+ },
+ "suggest": {
+ "ext-bcmath": "to improve IPV4 host parsing",
+ "ext-fileinfo": "to create Data URI from file contennts",
+ "ext-gmp": "to improve IPV4 host parsing",
+ "ext-intl": "to handle IDN host with the best performance",
+ "ext-mbstring": "to use the sorting algorithm of URLSearchParams",
+ "jeremykendall/php-domain-parser": "to further parse the URI host and resolve its Public Suffix and Top Level Domain",
+ "league/uri-polyfill": "to backport the PHP URI extension for older versions of PHP",
+ "php-64bit": "to improve IPV4 host parsing",
+ "rowbot/url": "to handle URLs using the WHATWG URL Living Standard specification",
+ "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "7.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "League\\Uri\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Ignace Nyamagana Butera",
+ "email": "nyamsprod@gmail.com",
+ "homepage": "https://nyamsprod.com"
+ }
+ ],
+ "description": "URI components manipulation library",
+ "homepage": "http://uri.thephpleague.com",
+ "keywords": [
+ "authority",
+ "components",
+ "fragment",
+ "host",
+ "middleware",
+ "modifier",
+ "path",
+ "port",
+ "query",
+ "rfc3986",
+ "scheme",
+ "uri",
+ "url",
+ "userinfo"
+ ],
+ "support": {
+ "docs": "https://uri.thephpleague.com",
+ "forum": "https://thephpleague.slack.com",
+ "issues": "https://github.com/thephpleague/uri-src/issues",
+ "source": "https://github.com/thephpleague/uri-components/tree/7.8.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/nyamsprod",
+ "type": "github"
+ }
+ ],
+ "time": "2026-01-14T17:24:56+00:00"
+ },
{
"name": "mockery/mockery",
"version": "1.6.12",
@@ -13003,6 +14411,89 @@
],
"time": "2025-08-20T13:10:51+00:00"
},
+ {
+ "name": "pestphp/pest-plugin-browser",
+ "version": "v4.2.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/pestphp/pest-plugin-browser.git",
+ "reference": "0ed837ab7e80e6fc78d36913cc0b006f8819336d"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/pestphp/pest-plugin-browser/zipball/0ed837ab7e80e6fc78d36913cc0b006f8819336d",
+ "reference": "0ed837ab7e80e6fc78d36913cc0b006f8819336d",
+ "shasum": ""
+ },
+ "require": {
+ "amphp/amp": "^3.1.1",
+ "amphp/http-server": "^3.4.3",
+ "amphp/websocket-client": "^2.0.2",
+ "ext-sockets": "*",
+ "pestphp/pest": "^4.3.1",
+ "pestphp/pest-plugin": "^4.0.0",
+ "php": "^8.3",
+ "symfony/process": "^7.4.3"
+ },
+ "require-dev": {
+ "ext-pcntl": "*",
+ "ext-posix": "*",
+ "livewire/livewire": "^3.7.3",
+ "nunomaduro/collision": "^8.8.3",
+ "orchestra/testbench": "^10.8.0",
+ "pestphp/pest-dev-tools": "^4.0.0",
+ "pestphp/pest-plugin-laravel": "^4.0",
+ "pestphp/pest-plugin-type-coverage": "^4.0.3"
+ },
+ "type": "library",
+ "extra": {
+ "pest": {
+ "plugins": [
+ "Pest\\Browser\\Plugin"
+ ]
+ }
+ },
+ "autoload": {
+ "files": [
+ "src/Autoload.php"
+ ],
+ "psr-4": {
+ "Pest\\Browser\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "Pest plugin to test browser interactions",
+ "keywords": [
+ "browser",
+ "framework",
+ "pest",
+ "php",
+ "test",
+ "testing",
+ "unit"
+ ],
+ "support": {
+ "source": "https://github.com/pestphp/pest-plugin-browser/tree/v4.2.1"
+ },
+ "funding": [
+ {
+ "url": "https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=66BYDWAT92N6L",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/nunomaduro",
+ "type": "github"
+ },
+ {
+ "url": "https://www.patreon.com/nunomaduro",
+ "type": "patreon"
+ }
+ ],
+ "time": "2026-01-11T20:32:34+00:00"
+ },
{
"name": "pestphp/pest-plugin-mutate",
"version": "v4.0.1",
@@ -13957,6 +15448,78 @@
],
"time": "2026-01-28T15:22:48+00:00"
},
+ {
+ "name": "revolt/event-loop",
+ "version": "v1.0.8",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/revoltphp/event-loop.git",
+ "reference": "b6fc06dce8e9b523c9946138fa5e62181934f91c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/revoltphp/event-loop/zipball/b6fc06dce8e9b523c9946138fa5e62181934f91c",
+ "reference": "b6fc06dce8e9b523c9946138fa5e62181934f91c",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1"
+ },
+ "require-dev": {
+ "ext-json": "*",
+ "jetbrains/phpstorm-stubs": "^2019.3",
+ "phpunit/phpunit": "^9",
+ "psalm/phar": "^5.15"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "1.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Revolt\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Aaron Piotrowski",
+ "email": "aaron@trowski.com"
+ },
+ {
+ "name": "Cees-Jan Kiewiet",
+ "email": "ceesjank@gmail.com"
+ },
+ {
+ "name": "Christian Lück",
+ "email": "christian@clue.engineering"
+ },
+ {
+ "name": "Niklas Keller",
+ "email": "me@kelunik.com"
+ }
+ ],
+ "description": "Rock-solid event loop for concurrent PHP applications.",
+ "keywords": [
+ "async",
+ "asynchronous",
+ "concurrency",
+ "event",
+ "event-loop",
+ "non-blocking",
+ "scheduler"
+ ],
+ "support": {
+ "issues": "https://github.com/revoltphp/event-loop/issues",
+ "source": "https://github.com/revoltphp/event-loop/tree/v1.0.8"
+ },
+ "time": "2025-08-27T21:33:23+00:00"
+ },
{
"name": "sebastian/cli-parser",
"version": "4.2.0",
diff --git a/config/database.php b/config/database.php
index 366ff90b5..79da0eaf7 100644
--- a/config/database.php
+++ b/config/database.php
@@ -54,18 +54,10 @@
],
'testing' => [
- 'driver' => 'pgsql',
- 'url' => env('DATABASE_TEST_URL'),
- 'host' => env('DB_TEST_HOST', 'postgres'),
- 'port' => env('DB_TEST_PORT', '5432'),
- 'database' => env('DB_TEST_DATABASE', 'coolify_test'),
- 'username' => env('DB_TEST_USERNAME', 'coolify'),
- 'password' => env('DB_TEST_PASSWORD', 'password'),
- 'charset' => 'utf8',
+ 'driver' => 'sqlite',
+ 'database' => ':memory:',
'prefix' => '',
- 'prefix_indexes' => true,
- 'search_path' => 'public',
- 'sslmode' => 'prefer',
+ 'foreign_key_constraints' => true,
],
],
diff --git a/database/migrations/2024_11_11_125366_add_index_to_activity_log.php b/database/migrations/2024_11_11_125366_add_index_to_activity_log.php
index 0c281ff40..5ebf62fe3 100644
--- a/database/migrations/2024_11_11_125366_add_index_to_activity_log.php
+++ b/database/migrations/2024_11_11_125366_add_index_to_activity_log.php
@@ -8,6 +8,10 @@ class AddIndexToActivityLog extends Migration
{
public function up()
{
+ if (DB::connection()->getDriverName() !== 'pgsql') {
+ return;
+ }
+
try {
DB::statement('ALTER TABLE activity_log ALTER COLUMN properties TYPE jsonb USING properties::jsonb');
DB::statement('CREATE INDEX idx_activity_type_uuid ON activity_log USING GIN (properties jsonb_path_ops)');
@@ -18,6 +22,10 @@ public function up()
public function down()
{
+ if (DB::connection()->getDriverName() !== 'pgsql') {
+ return;
+ }
+
try {
DB::statement('DROP INDEX IF EXISTS idx_activity_type_uuid');
DB::statement('ALTER TABLE activity_log ALTER COLUMN properties TYPE json USING properties::json');
diff --git a/database/migrations/2025_12_08_135600_add_performance_indexes.php b/database/migrations/2025_12_08_135600_add_performance_indexes.php
index 680c4b4f7..ce38d7cc2 100644
--- a/database/migrations/2025_12_08_135600_add_performance_indexes.php
+++ b/database/migrations/2025_12_08_135600_add_performance_indexes.php
@@ -22,6 +22,10 @@
public function up(): void
{
+ if (DB::connection()->getDriverName() !== 'pgsql') {
+ return;
+ }
+
foreach ($this->indexes as [$table, $columns, $indexName]) {
if (! $this->indexExists($indexName)) {
$columnList = implode(', ', array_map(fn ($col) => "\"$col\"", $columns));
@@ -32,6 +36,10 @@ public function up(): void
public function down(): void
{
+ if (DB::connection()->getDriverName() !== 'pgsql') {
+ return;
+ }
+
foreach ($this->indexes as [, , $indexName]) {
DB::statement("DROP INDEX IF EXISTS \"{$indexName}\"");
}
diff --git a/database/schema/testing-schema.sql b/database/schema/testing-schema.sql
new file mode 100644
index 000000000..edbc35db4
--- /dev/null
+++ b/database/schema/testing-schema.sql
@@ -0,0 +1,1754 @@
+-- Generated by: php artisan schema:generate-testing
+-- Date: 2026-02-11 13:10:01
+-- Last migration: 2025_12_17_000002_add_restart_tracking_to_standalone_databases
+
+CREATE TABLE IF NOT EXISTS "activity_log" (
+ "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+ "log_name" TEXT,
+ "description" TEXT NOT NULL,
+ "subject_type" TEXT,
+ "subject_id" INTEGER,
+ "causer_type" TEXT,
+ "causer_id" INTEGER,
+ "properties" TEXT,
+ "created_at" TEXT,
+ "updated_at" TEXT,
+ "event" TEXT,
+ "batch_uuid" TEXT
+);
+
+CREATE TABLE IF NOT EXISTS "additional_destinations" (
+ "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+ "application_id" INTEGER NOT NULL,
+ "server_id" INTEGER NOT NULL,
+ "status" TEXT DEFAULT 'exited' NOT NULL,
+ "standalone_docker_id" INTEGER NOT NULL,
+ "created_at" TEXT,
+ "updated_at" TEXT
+);
+
+CREATE TABLE IF NOT EXISTS "application_deployment_queues" (
+ "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+ "application_id" TEXT NOT NULL,
+ "deployment_uuid" TEXT NOT NULL,
+ "pull_request_id" INTEGER DEFAULT 0 NOT NULL,
+ "force_rebuild" INTEGER DEFAULT false NOT NULL,
+ "commit" TEXT DEFAULT 'HEAD' NOT NULL,
+ "status" TEXT DEFAULT 'queued' NOT NULL,
+ "is_webhook" INTEGER DEFAULT false NOT NULL,
+ "created_at" TEXT,
+ "updated_at" TEXT,
+ "logs" TEXT,
+ "current_process_id" TEXT,
+ "restart_only" INTEGER DEFAULT false NOT NULL,
+ "git_type" TEXT,
+ "server_id" INTEGER,
+ "application_name" TEXT,
+ "server_name" TEXT,
+ "deployment_url" TEXT,
+ "destination_id" TEXT,
+ "only_this_server" INTEGER DEFAULT false NOT NULL,
+ "rollback" INTEGER DEFAULT false NOT NULL,
+ "commit_message" TEXT,
+ "is_api" INTEGER DEFAULT false NOT NULL,
+ "build_server_id" INTEGER,
+ "horizon_job_id" TEXT,
+ "horizon_job_worker" TEXT,
+ "finished_at" TEXT
+);
+
+CREATE TABLE IF NOT EXISTS "application_previews" (
+ "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+ "uuid" TEXT NOT NULL,
+ "pull_request_id" INTEGER NOT NULL,
+ "pull_request_html_url" TEXT NOT NULL,
+ "pull_request_issue_comment_id" TEXT,
+ "fqdn" TEXT,
+ "status" TEXT DEFAULT 'exited' NOT NULL,
+ "application_id" INTEGER NOT NULL,
+ "created_at" TEXT,
+ "updated_at" TEXT,
+ "git_type" TEXT,
+ "docker_compose_domains" TEXT,
+ "last_online_at" TEXT DEFAULT '2026-02-11 12:51:02' NOT NULL,
+ "deleted_at" TEXT
+);
+
+CREATE TABLE IF NOT EXISTS "application_settings" (
+ "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+ "is_static" INTEGER DEFAULT false NOT NULL,
+ "is_git_submodules_enabled" INTEGER DEFAULT true NOT NULL,
+ "is_git_lfs_enabled" INTEGER DEFAULT true NOT NULL,
+ "is_auto_deploy_enabled" INTEGER DEFAULT true NOT NULL,
+ "is_force_https_enabled" INTEGER DEFAULT true NOT NULL,
+ "is_debug_enabled" INTEGER DEFAULT false NOT NULL,
+ "is_preview_deployments_enabled" INTEGER DEFAULT false NOT NULL,
+ "application_id" INTEGER NOT NULL,
+ "created_at" TEXT,
+ "updated_at" TEXT,
+ "is_log_drain_enabled" INTEGER DEFAULT false NOT NULL,
+ "is_gpu_enabled" INTEGER DEFAULT false NOT NULL,
+ "gpu_driver" TEXT DEFAULT 'nvidia' NOT NULL,
+ "gpu_count" TEXT,
+ "gpu_device_ids" TEXT,
+ "gpu_options" TEXT,
+ "is_include_timestamps" INTEGER DEFAULT false NOT NULL,
+ "is_swarm_only_worker_nodes" INTEGER DEFAULT true NOT NULL,
+ "is_raw_compose_deployment_enabled" INTEGER DEFAULT false NOT NULL,
+ "is_build_server_enabled" INTEGER DEFAULT false NOT NULL,
+ "is_consistent_container_name_enabled" INTEGER DEFAULT false NOT NULL,
+ "is_gzip_enabled" INTEGER DEFAULT true NOT NULL,
+ "is_stripprefix_enabled" INTEGER DEFAULT true NOT NULL,
+ "connect_to_docker_network" INTEGER DEFAULT false NOT NULL,
+ "custom_internal_name" TEXT,
+ "is_container_label_escape_enabled" INTEGER DEFAULT true NOT NULL,
+ "is_env_sorting_enabled" INTEGER DEFAULT false NOT NULL,
+ "is_container_label_readonly_enabled" INTEGER DEFAULT true NOT NULL,
+ "is_preserve_repository_enabled" INTEGER DEFAULT false NOT NULL,
+ "disable_build_cache" INTEGER DEFAULT false NOT NULL,
+ "is_spa" INTEGER DEFAULT false NOT NULL,
+ "is_git_shallow_clone_enabled" INTEGER DEFAULT true NOT NULL,
+ "is_pr_deployments_public_enabled" INTEGER DEFAULT false NOT NULL,
+ "use_build_secrets" INTEGER DEFAULT false NOT NULL,
+ "inject_build_args_to_dockerfile" INTEGER DEFAULT true NOT NULL,
+ "include_source_commit_in_build" INTEGER DEFAULT false NOT NULL,
+ "docker_images_to_keep" INTEGER DEFAULT 2 NOT NULL
+);
+
+CREATE TABLE IF NOT EXISTS "applications" (
+ "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+ "repository_project_id" INTEGER,
+ "uuid" TEXT NOT NULL,
+ "name" TEXT NOT NULL,
+ "fqdn" TEXT,
+ "config_hash" TEXT,
+ "git_repository" TEXT NOT NULL,
+ "git_branch" TEXT NOT NULL,
+ "git_commit_sha" TEXT DEFAULT 'HEAD' NOT NULL,
+ "git_full_url" TEXT,
+ "docker_registry_image_name" TEXT,
+ "docker_registry_image_tag" TEXT,
+ "build_pack" TEXT NOT NULL,
+ "static_image" TEXT DEFAULT 'nginx:alpine' NOT NULL,
+ "install_command" TEXT,
+ "build_command" TEXT,
+ "start_command" TEXT,
+ "ports_exposes" TEXT NOT NULL,
+ "ports_mappings" TEXT,
+ "base_directory" TEXT DEFAULT '/' NOT NULL,
+ "publish_directory" TEXT,
+ "health_check_path" TEXT DEFAULT '/' NOT NULL,
+ "health_check_port" TEXT,
+ "health_check_host" TEXT DEFAULT 'localhost' NOT NULL,
+ "health_check_method" TEXT DEFAULT 'GET' NOT NULL,
+ "health_check_return_code" INTEGER DEFAULT 200 NOT NULL,
+ "health_check_scheme" TEXT DEFAULT 'http' NOT NULL,
+ "health_check_response_text" TEXT,
+ "health_check_interval" INTEGER DEFAULT 5 NOT NULL,
+ "health_check_timeout" INTEGER DEFAULT 5 NOT NULL,
+ "health_check_retries" INTEGER DEFAULT 10 NOT NULL,
+ "health_check_start_period" INTEGER DEFAULT 5 NOT NULL,
+ "limits_memory" TEXT DEFAULT '0' NOT NULL,
+ "limits_memory_swap" TEXT DEFAULT '0' NOT NULL,
+ "limits_memory_swappiness" INTEGER DEFAULT 60 NOT NULL,
+ "limits_memory_reservation" TEXT DEFAULT '0' NOT NULL,
+ "limits_cpus" TEXT DEFAULT '0' NOT NULL,
+ "limits_cpuset" TEXT,
+ "limits_cpu_shares" INTEGER DEFAULT 1024 NOT NULL,
+ "status" TEXT DEFAULT 'exited' NOT NULL,
+ "preview_url_template" TEXT DEFAULT '{{pr_id}}.{{domain}}' NOT NULL,
+ "destination_type" TEXT,
+ "destination_id" INTEGER,
+ "source_type" TEXT,
+ "source_id" INTEGER,
+ "private_key_id" INTEGER,
+ "environment_id" INTEGER NOT NULL,
+ "created_at" TEXT,
+ "updated_at" TEXT,
+ "description" TEXT,
+ "dockerfile" TEXT,
+ "health_check_enabled" INTEGER DEFAULT false NOT NULL,
+ "dockerfile_location" TEXT,
+ "custom_labels" TEXT,
+ "dockerfile_target_build" TEXT,
+ "manual_webhook_secret_github" TEXT,
+ "manual_webhook_secret_gitlab" TEXT,
+ "docker_compose_location" TEXT DEFAULT '/docker-compose.yaml',
+ "docker_compose" TEXT,
+ "docker_compose_raw" TEXT,
+ "docker_compose_domains" TEXT,
+ "deleted_at" TEXT,
+ "docker_compose_custom_start_command" TEXT,
+ "docker_compose_custom_build_command" TEXT,
+ "swarm_replicas" INTEGER DEFAULT 1 NOT NULL,
+ "swarm_placement_constraints" TEXT,
+ "manual_webhook_secret_bitbucket" TEXT,
+ "custom_docker_run_options" TEXT,
+ "post_deployment_command" TEXT,
+ "post_deployment_command_container" TEXT,
+ "pre_deployment_command" TEXT,
+ "pre_deployment_command_container" TEXT,
+ "watch_paths" TEXT,
+ "custom_healthcheck_found" INTEGER DEFAULT false NOT NULL,
+ "manual_webhook_secret_gitea" TEXT,
+ "redirect" TEXT DEFAULT 'both' NOT NULL,
+ "compose_parsing_version" TEXT DEFAULT '1' NOT NULL,
+ "last_online_at" TEXT DEFAULT '2026-02-11 12:51:02' NOT NULL,
+ "custom_nginx_configuration" TEXT,
+ "custom_network_aliases" TEXT,
+ "is_http_basic_auth_enabled" INTEGER DEFAULT false NOT NULL,
+ "http_basic_auth_username" TEXT,
+ "http_basic_auth_password" TEXT,
+ "restart_count" INTEGER DEFAULT 0 NOT NULL,
+ "last_restart_at" TEXT,
+ "last_restart_type" TEXT
+);
+
+CREATE TABLE IF NOT EXISTS "cloud_init_scripts" (
+ "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+ "team_id" INTEGER NOT NULL,
+ "name" TEXT NOT NULL,
+ "script" TEXT NOT NULL,
+ "created_at" TEXT,
+ "updated_at" TEXT
+);
+
+CREATE TABLE IF NOT EXISTS "cloud_provider_tokens" (
+ "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+ "team_id" INTEGER NOT NULL,
+ "provider" TEXT NOT NULL,
+ "token" TEXT NOT NULL,
+ "name" TEXT,
+ "created_at" TEXT,
+ "updated_at" TEXT,
+ "uuid" TEXT NOT NULL
+);
+
+CREATE TABLE IF NOT EXISTS "discord_notification_settings" (
+ "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+ "team_id" INTEGER NOT NULL,
+ "discord_enabled" INTEGER DEFAULT false NOT NULL,
+ "discord_webhook_url" TEXT,
+ "deployment_success_discord_notifications" INTEGER DEFAULT false NOT NULL,
+ "deployment_failure_discord_notifications" INTEGER DEFAULT true NOT NULL,
+ "status_change_discord_notifications" INTEGER DEFAULT false NOT NULL,
+ "backup_success_discord_notifications" INTEGER DEFAULT false NOT NULL,
+ "backup_failure_discord_notifications" INTEGER DEFAULT true NOT NULL,
+ "scheduled_task_success_discord_notifications" INTEGER DEFAULT false NOT NULL,
+ "scheduled_task_failure_discord_notifications" INTEGER DEFAULT true NOT NULL,
+ "docker_cleanup_success_discord_notifications" INTEGER DEFAULT false NOT NULL,
+ "docker_cleanup_failure_discord_notifications" INTEGER DEFAULT true NOT NULL,
+ "server_disk_usage_discord_notifications" INTEGER DEFAULT true NOT NULL,
+ "server_reachable_discord_notifications" INTEGER DEFAULT false NOT NULL,
+ "server_unreachable_discord_notifications" INTEGER DEFAULT true NOT NULL,
+ "discord_ping_enabled" INTEGER DEFAULT true NOT NULL,
+ "server_patch_discord_notifications" INTEGER DEFAULT true NOT NULL,
+ "traefik_outdated_discord_notifications" INTEGER DEFAULT true NOT NULL
+);
+
+CREATE TABLE IF NOT EXISTS "docker_cleanup_executions" (
+ "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+ "uuid" TEXT NOT NULL,
+ "status" TEXT DEFAULT 'running' NOT NULL,
+ "message" TEXT,
+ "cleanup_log" TEXT,
+ "server_id" INTEGER NOT NULL,
+ "created_at" TEXT,
+ "updated_at" TEXT,
+ "finished_at" TEXT
+);
+
+CREATE TABLE IF NOT EXISTS "email_notification_settings" (
+ "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+ "team_id" INTEGER NOT NULL,
+ "smtp_enabled" INTEGER DEFAULT false NOT NULL,
+ "smtp_from_address" TEXT,
+ "smtp_from_name" TEXT,
+ "smtp_recipients" TEXT,
+ "smtp_host" TEXT,
+ "smtp_port" INTEGER,
+ "smtp_encryption" TEXT,
+ "smtp_username" TEXT,
+ "smtp_password" TEXT,
+ "smtp_timeout" INTEGER,
+ "resend_enabled" INTEGER DEFAULT false NOT NULL,
+ "resend_api_key" TEXT,
+ "use_instance_email_settings" INTEGER DEFAULT false NOT NULL,
+ "deployment_success_email_notifications" INTEGER DEFAULT false NOT NULL,
+ "deployment_failure_email_notifications" INTEGER DEFAULT true NOT NULL,
+ "status_change_email_notifications" INTEGER DEFAULT false NOT NULL,
+ "backup_success_email_notifications" INTEGER DEFAULT false NOT NULL,
+ "backup_failure_email_notifications" INTEGER DEFAULT true NOT NULL,
+ "scheduled_task_success_email_notifications" INTEGER DEFAULT false NOT NULL,
+ "scheduled_task_failure_email_notifications" INTEGER DEFAULT true NOT NULL,
+ "docker_cleanup_success_email_notifications" INTEGER DEFAULT false NOT NULL,
+ "docker_cleanup_failure_email_notifications" INTEGER DEFAULT true NOT NULL,
+ "server_disk_usage_email_notifications" INTEGER DEFAULT true NOT NULL,
+ "server_reachable_email_notifications" INTEGER DEFAULT false NOT NULL,
+ "server_unreachable_email_notifications" INTEGER DEFAULT true NOT NULL,
+ "server_patch_email_notifications" INTEGER DEFAULT true NOT NULL,
+ "traefik_outdated_email_notifications" INTEGER DEFAULT true NOT NULL
+);
+
+CREATE TABLE IF NOT EXISTS "environment_variables" (
+ "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+ "key" TEXT NOT NULL,
+ "value" TEXT,
+ "is_preview" INTEGER DEFAULT false NOT NULL,
+ "created_at" TEXT,
+ "updated_at" TEXT,
+ "is_shown_once" INTEGER DEFAULT false NOT NULL,
+ "is_multiline" INTEGER DEFAULT false NOT NULL,
+ "version" TEXT DEFAULT '4.0.0-beta.239' NOT NULL,
+ "is_literal" INTEGER DEFAULT false NOT NULL,
+ "uuid" TEXT NOT NULL,
+ "order" INTEGER,
+ "is_required" INTEGER DEFAULT false NOT NULL,
+ "is_shared" INTEGER DEFAULT false NOT NULL,
+ "resourceable_type" TEXT,
+ "resourceable_id" INTEGER,
+ "is_runtime" INTEGER DEFAULT true NOT NULL,
+ "is_buildtime" INTEGER DEFAULT true NOT NULL
+);
+
+CREATE TABLE IF NOT EXISTS "environments" (
+ "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+ "name" TEXT NOT NULL,
+ "project_id" INTEGER NOT NULL,
+ "created_at" TEXT,
+ "updated_at" TEXT,
+ "description" TEXT,
+ "uuid" TEXT NOT NULL
+);
+
+CREATE TABLE IF NOT EXISTS "failed_jobs" (
+ "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+ "uuid" TEXT NOT NULL,
+ "connection" TEXT NOT NULL,
+ "queue" TEXT NOT NULL,
+ "payload" TEXT NOT NULL,
+ "exception" TEXT NOT NULL,
+ "failed_at" TEXT DEFAULT CURRENT_TIMESTAMP NOT NULL
+);
+
+CREATE TABLE IF NOT EXISTS "github_apps" (
+ "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+ "uuid" TEXT NOT NULL,
+ "name" TEXT NOT NULL,
+ "organization" TEXT,
+ "api_url" TEXT NOT NULL,
+ "html_url" TEXT NOT NULL,
+ "custom_user" TEXT DEFAULT 'git' NOT NULL,
+ "custom_port" INTEGER DEFAULT 22 NOT NULL,
+ "app_id" INTEGER,
+ "installation_id" INTEGER,
+ "client_id" TEXT,
+ "client_secret" TEXT,
+ "webhook_secret" TEXT,
+ "is_system_wide" INTEGER DEFAULT false NOT NULL,
+ "is_public" INTEGER DEFAULT false NOT NULL,
+ "private_key_id" INTEGER,
+ "team_id" INTEGER NOT NULL,
+ "created_at" TEXT,
+ "updated_at" TEXT,
+ "contents" TEXT,
+ "metadata" TEXT,
+ "pull_requests" TEXT,
+ "administration" TEXT
+);
+
+CREATE TABLE IF NOT EXISTS "gitlab_apps" (
+ "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+ "uuid" TEXT NOT NULL,
+ "name" TEXT NOT NULL,
+ "organization" TEXT,
+ "api_url" TEXT NOT NULL,
+ "html_url" TEXT NOT NULL,
+ "custom_port" INTEGER DEFAULT 22 NOT NULL,
+ "custom_user" TEXT DEFAULT 'git' NOT NULL,
+ "is_system_wide" INTEGER DEFAULT false NOT NULL,
+ "is_public" INTEGER DEFAULT false NOT NULL,
+ "app_id" INTEGER,
+ "app_secret" TEXT,
+ "oauth_id" INTEGER,
+ "group_name" TEXT,
+ "public_key" TEXT,
+ "webhook_token" TEXT,
+ "deploy_key_id" INTEGER,
+ "private_key_id" INTEGER,
+ "team_id" INTEGER NOT NULL,
+ "created_at" TEXT,
+ "updated_at" TEXT
+);
+
+CREATE TABLE IF NOT EXISTS "instance_settings" (
+ "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+ "public_ipv4" TEXT,
+ "public_ipv6" TEXT,
+ "fqdn" TEXT,
+ "public_port_min" INTEGER DEFAULT 9000 NOT NULL,
+ "public_port_max" INTEGER DEFAULT 9100 NOT NULL,
+ "do_not_track" INTEGER DEFAULT false NOT NULL,
+ "is_auto_update_enabled" INTEGER DEFAULT true NOT NULL,
+ "is_registration_enabled" INTEGER DEFAULT true NOT NULL,
+ "created_at" TEXT,
+ "updated_at" TEXT,
+ "next_channel" INTEGER DEFAULT false NOT NULL,
+ "smtp_enabled" INTEGER DEFAULT false NOT NULL,
+ "smtp_from_address" TEXT,
+ "smtp_from_name" TEXT,
+ "smtp_recipients" TEXT,
+ "smtp_host" TEXT,
+ "smtp_port" INTEGER,
+ "smtp_encryption" TEXT,
+ "smtp_username" TEXT,
+ "smtp_password" TEXT,
+ "smtp_timeout" INTEGER,
+ "resend_enabled" INTEGER DEFAULT false NOT NULL,
+ "resend_api_key" TEXT,
+ "is_dns_validation_enabled" INTEGER DEFAULT true NOT NULL,
+ "custom_dns_servers" TEXT DEFAULT '1.1.1.1',
+ "instance_name" TEXT,
+ "is_api_enabled" INTEGER DEFAULT false NOT NULL,
+ "allowed_ips" TEXT,
+ "auto_update_frequency" TEXT DEFAULT '0 0 * * *' NOT NULL,
+ "update_check_frequency" TEXT DEFAULT '0 * * * *' NOT NULL,
+ "new_version_available" INTEGER DEFAULT false NOT NULL,
+ "instance_timezone" TEXT DEFAULT 'UTC' NOT NULL,
+ "helper_version" TEXT DEFAULT '1.0.0' NOT NULL,
+ "disable_two_step_confirmation" INTEGER DEFAULT false NOT NULL,
+ "is_sponsorship_popup_enabled" INTEGER DEFAULT true NOT NULL,
+ "dev_helper_version" TEXT,
+ "is_wire_navigate_enabled" INTEGER DEFAULT true NOT NULL
+);
+
+CREATE TABLE IF NOT EXISTS "local_file_volumes" (
+ "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+ "uuid" TEXT NOT NULL,
+ "fs_path" TEXT NOT NULL,
+ "mount_path" TEXT,
+ "content" TEXT,
+ "resource_type" TEXT,
+ "resource_id" INTEGER,
+ "created_at" TEXT,
+ "updated_at" TEXT,
+ "is_directory" INTEGER DEFAULT false NOT NULL,
+ "chown" TEXT,
+ "chmod" TEXT,
+ "is_based_on_git" INTEGER DEFAULT false NOT NULL
+);
+
+CREATE TABLE IF NOT EXISTS "local_persistent_volumes" (
+ "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+ "name" TEXT NOT NULL,
+ "mount_path" TEXT NOT NULL,
+ "host_path" TEXT,
+ "container_id" TEXT,
+ "resource_type" TEXT,
+ "resource_id" INTEGER,
+ "created_at" TEXT,
+ "updated_at" TEXT
+);
+
+CREATE TABLE IF NOT EXISTS "migrations" (
+ "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+ "migration" TEXT NOT NULL,
+ "batch" INTEGER NOT NULL
+);
+
+CREATE TABLE IF NOT EXISTS "oauth_settings" (
+ "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+ "provider" TEXT NOT NULL,
+ "enabled" INTEGER DEFAULT false NOT NULL,
+ "client_id" TEXT,
+ "client_secret" TEXT,
+ "redirect_uri" TEXT,
+ "tenant" TEXT,
+ "created_at" TEXT,
+ "updated_at" TEXT,
+ "base_url" TEXT
+);
+
+CREATE TABLE IF NOT EXISTS "password_reset_tokens" (
+ "email" TEXT NOT NULL,
+ "token" TEXT NOT NULL,
+ "created_at" TEXT
+);
+
+CREATE TABLE IF NOT EXISTS "personal_access_tokens" (
+ "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+ "tokenable_type" TEXT NOT NULL,
+ "tokenable_id" INTEGER NOT NULL,
+ "name" TEXT NOT NULL,
+ "token" TEXT NOT NULL,
+ "team_id" TEXT NOT NULL,
+ "abilities" TEXT,
+ "last_used_at" TEXT,
+ "expires_at" TEXT,
+ "created_at" TEXT,
+ "updated_at" TEXT
+);
+
+CREATE TABLE IF NOT EXISTS "private_keys" (
+ "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+ "uuid" TEXT NOT NULL,
+ "name" TEXT NOT NULL,
+ "description" TEXT,
+ "private_key" TEXT NOT NULL,
+ "is_git_related" INTEGER DEFAULT false NOT NULL,
+ "team_id" INTEGER NOT NULL,
+ "created_at" TEXT,
+ "updated_at" TEXT,
+ "fingerprint" TEXT
+);
+
+CREATE TABLE IF NOT EXISTS "project_settings" (
+ "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+ "project_id" INTEGER NOT NULL,
+ "created_at" TEXT,
+ "updated_at" TEXT
+);
+
+CREATE TABLE IF NOT EXISTS "projects" (
+ "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+ "uuid" TEXT NOT NULL,
+ "name" TEXT NOT NULL,
+ "description" TEXT,
+ "team_id" INTEGER NOT NULL,
+ "created_at" TEXT,
+ "updated_at" TEXT
+);
+
+CREATE TABLE IF NOT EXISTS "pushover_notification_settings" (
+ "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+ "team_id" INTEGER NOT NULL,
+ "pushover_enabled" INTEGER DEFAULT false NOT NULL,
+ "pushover_user_key" TEXT,
+ "pushover_api_token" TEXT,
+ "deployment_success_pushover_notifications" INTEGER DEFAULT false NOT NULL,
+ "deployment_failure_pushover_notifications" INTEGER DEFAULT true NOT NULL,
+ "status_change_pushover_notifications" INTEGER DEFAULT false NOT NULL,
+ "backup_success_pushover_notifications" INTEGER DEFAULT false NOT NULL,
+ "backup_failure_pushover_notifications" INTEGER DEFAULT true NOT NULL,
+ "scheduled_task_success_pushover_notifications" INTEGER DEFAULT false NOT NULL,
+ "scheduled_task_failure_pushover_notifications" INTEGER DEFAULT true NOT NULL,
+ "docker_cleanup_success_pushover_notifications" INTEGER DEFAULT false NOT NULL,
+ "docker_cleanup_failure_pushover_notifications" INTEGER DEFAULT true NOT NULL,
+ "server_disk_usage_pushover_notifications" INTEGER DEFAULT true NOT NULL,
+ "server_reachable_pushover_notifications" INTEGER DEFAULT false NOT NULL,
+ "server_unreachable_pushover_notifications" INTEGER DEFAULT true NOT NULL,
+ "server_patch_pushover_notifications" INTEGER DEFAULT true NOT NULL,
+ "traefik_outdated_pushover_notifications" INTEGER DEFAULT true NOT NULL
+);
+
+CREATE TABLE IF NOT EXISTS "s3_storages" (
+ "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+ "uuid" TEXT NOT NULL,
+ "name" TEXT NOT NULL,
+ "description" TEXT,
+ "region" TEXT DEFAULT 'us-east-1' NOT NULL,
+ "key" TEXT NOT NULL,
+ "secret" TEXT NOT NULL,
+ "bucket" TEXT NOT NULL,
+ "endpoint" TEXT,
+ "team_id" INTEGER NOT NULL,
+ "created_at" TEXT,
+ "updated_at" TEXT,
+ "is_usable" INTEGER DEFAULT false NOT NULL,
+ "unusable_email_sent" INTEGER DEFAULT false NOT NULL
+);
+
+CREATE TABLE IF NOT EXISTS "scheduled_database_backup_executions" (
+ "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+ "uuid" TEXT NOT NULL,
+ "status" TEXT DEFAULT 'running' NOT NULL,
+ "message" TEXT,
+ "size" TEXT,
+ "filename" TEXT,
+ "scheduled_database_backup_id" INTEGER NOT NULL,
+ "created_at" TEXT,
+ "updated_at" TEXT,
+ "database_name" TEXT,
+ "finished_at" TEXT,
+ "local_storage_deleted" INTEGER DEFAULT false NOT NULL,
+ "s3_storage_deleted" INTEGER DEFAULT false NOT NULL,
+ "s3_uploaded" INTEGER
+);
+
+CREATE TABLE IF NOT EXISTS "scheduled_database_backups" (
+ "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+ "description" TEXT,
+ "uuid" TEXT NOT NULL,
+ "enabled" INTEGER DEFAULT true NOT NULL,
+ "save_s3" INTEGER DEFAULT true NOT NULL,
+ "frequency" TEXT NOT NULL,
+ "database_backup_retention_amount_locally" INTEGER DEFAULT 0 NOT NULL,
+ "database_type" TEXT NOT NULL,
+ "database_id" INTEGER NOT NULL,
+ "s3_storage_id" INTEGER,
+ "team_id" INTEGER NOT NULL,
+ "created_at" TEXT,
+ "updated_at" TEXT,
+ "databases_to_backup" TEXT,
+ "dump_all" INTEGER DEFAULT false NOT NULL,
+ "database_backup_retention_days_locally" INTEGER DEFAULT 0 NOT NULL,
+ "database_backup_retention_max_storage_locally" REAL DEFAULT '0' NOT NULL,
+ "database_backup_retention_amount_s3" INTEGER DEFAULT 0 NOT NULL,
+ "database_backup_retention_days_s3" INTEGER DEFAULT 0 NOT NULL,
+ "database_backup_retention_max_storage_s3" REAL DEFAULT '0' NOT NULL,
+ "timeout" INTEGER DEFAULT 3600 NOT NULL,
+ "disable_local_backup" INTEGER DEFAULT false NOT NULL
+);
+
+CREATE TABLE IF NOT EXISTS "scheduled_task_executions" (
+ "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+ "uuid" TEXT NOT NULL,
+ "status" TEXT DEFAULT 'running' NOT NULL,
+ "message" TEXT,
+ "scheduled_task_id" INTEGER NOT NULL,
+ "created_at" TEXT,
+ "updated_at" TEXT,
+ "finished_at" TEXT,
+ "started_at" TEXT,
+ "retry_count" INTEGER DEFAULT 0 NOT NULL,
+ "duration" REAL,
+ "error_details" TEXT
+);
+
+CREATE TABLE IF NOT EXISTS "scheduled_tasks" (
+ "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+ "uuid" TEXT NOT NULL,
+ "enabled" INTEGER DEFAULT true NOT NULL,
+ "name" TEXT NOT NULL,
+ "command" TEXT NOT NULL,
+ "frequency" TEXT NOT NULL,
+ "container" TEXT,
+ "created_at" TEXT,
+ "updated_at" TEXT,
+ "application_id" INTEGER,
+ "service_id" INTEGER,
+ "team_id" INTEGER NOT NULL,
+ "timeout" INTEGER DEFAULT 300 NOT NULL
+);
+
+CREATE TABLE IF NOT EXISTS "server_settings" (
+ "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+ "is_swarm_manager" INTEGER DEFAULT false NOT NULL,
+ "is_jump_server" INTEGER DEFAULT false NOT NULL,
+ "is_build_server" INTEGER DEFAULT false NOT NULL,
+ "is_reachable" INTEGER DEFAULT false NOT NULL,
+ "is_usable" INTEGER DEFAULT false NOT NULL,
+ "server_id" INTEGER NOT NULL,
+ "created_at" TEXT,
+ "updated_at" TEXT,
+ "wildcard_domain" TEXT,
+ "is_cloudflare_tunnel" INTEGER DEFAULT false NOT NULL,
+ "is_logdrain_newrelic_enabled" INTEGER DEFAULT false NOT NULL,
+ "logdrain_newrelic_license_key" TEXT,
+ "logdrain_newrelic_base_uri" TEXT,
+ "is_logdrain_highlight_enabled" INTEGER DEFAULT false NOT NULL,
+ "logdrain_highlight_project_id" TEXT,
+ "is_logdrain_axiom_enabled" INTEGER DEFAULT false NOT NULL,
+ "logdrain_axiom_dataset_name" TEXT,
+ "logdrain_axiom_api_key" TEXT,
+ "is_swarm_worker" INTEGER DEFAULT false NOT NULL,
+ "is_logdrain_custom_enabled" INTEGER DEFAULT false NOT NULL,
+ "logdrain_custom_config" TEXT,
+ "logdrain_custom_config_parser" TEXT,
+ "concurrent_builds" INTEGER DEFAULT 2 NOT NULL,
+ "dynamic_timeout" INTEGER DEFAULT 3600 NOT NULL,
+ "force_disabled" INTEGER DEFAULT false NOT NULL,
+ "is_metrics_enabled" INTEGER DEFAULT false NOT NULL,
+ "generate_exact_labels" INTEGER DEFAULT false NOT NULL,
+ "force_docker_cleanup" INTEGER DEFAULT true NOT NULL,
+ "docker_cleanup_frequency" TEXT DEFAULT '0 0 * * *' NOT NULL,
+ "docker_cleanup_threshold" INTEGER DEFAULT 80 NOT NULL,
+ "server_timezone" TEXT DEFAULT 'UTC' NOT NULL,
+ "delete_unused_volumes" INTEGER DEFAULT false NOT NULL,
+ "delete_unused_networks" INTEGER DEFAULT false NOT NULL,
+ "is_sentinel_enabled" INTEGER DEFAULT true NOT NULL,
+ "sentinel_token" TEXT,
+ "sentinel_metrics_refresh_rate_seconds" INTEGER DEFAULT 10 NOT NULL,
+ "sentinel_metrics_history_days" INTEGER DEFAULT 7 NOT NULL,
+ "sentinel_push_interval_seconds" INTEGER DEFAULT 60 NOT NULL,
+ "sentinel_custom_url" TEXT,
+ "server_disk_usage_notification_threshold" INTEGER DEFAULT 80 NOT NULL,
+ "is_sentinel_debug_enabled" INTEGER DEFAULT false NOT NULL,
+ "server_disk_usage_check_frequency" TEXT DEFAULT '0 23 * * *' NOT NULL,
+ "is_terminal_enabled" INTEGER DEFAULT true NOT NULL,
+ "deployment_queue_limit" INTEGER DEFAULT 25 NOT NULL,
+ "disable_application_image_retention" INTEGER DEFAULT false NOT NULL
+);
+
+CREATE TABLE IF NOT EXISTS "servers" (
+ "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+ "uuid" TEXT NOT NULL,
+ "name" TEXT NOT NULL,
+ "description" TEXT,
+ "ip" TEXT NOT NULL,
+ "port" INTEGER DEFAULT 22 NOT NULL,
+ "user" TEXT DEFAULT 'root' NOT NULL,
+ "team_id" INTEGER NOT NULL,
+ "private_key_id" INTEGER NOT NULL,
+ "proxy" TEXT,
+ "created_at" TEXT,
+ "updated_at" TEXT,
+ "unreachable_notification_sent" INTEGER DEFAULT false NOT NULL,
+ "unreachable_count" INTEGER DEFAULT 0 NOT NULL,
+ "high_disk_usage_notification_sent" INTEGER DEFAULT false NOT NULL,
+ "log_drain_notification_sent" INTEGER DEFAULT false NOT NULL,
+ "swarm_cluster" INTEGER,
+ "validation_logs" TEXT,
+ "sentinel_updated_at" TEXT DEFAULT '2026-02-11 12:51:02' NOT NULL,
+ "deleted_at" TEXT,
+ "ip_previous" TEXT,
+ "hetzner_server_id" INTEGER,
+ "cloud_provider_token_id" INTEGER,
+ "hetzner_server_status" TEXT,
+ "is_validating" INTEGER DEFAULT false NOT NULL,
+ "detected_traefik_version" TEXT,
+ "traefik_outdated_info" TEXT
+);
+
+CREATE TABLE IF NOT EXISTS "service_applications" (
+ "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+ "uuid" TEXT NOT NULL,
+ "name" TEXT NOT NULL,
+ "human_name" TEXT,
+ "description" TEXT,
+ "fqdn" TEXT,
+ "ports" TEXT,
+ "exposes" TEXT,
+ "status" TEXT DEFAULT 'exited' NOT NULL,
+ "service_id" INTEGER NOT NULL,
+ "created_at" TEXT,
+ "updated_at" TEXT,
+ "exclude_from_status" INTEGER DEFAULT false NOT NULL,
+ "required_fqdn" INTEGER DEFAULT false NOT NULL,
+ "image" TEXT,
+ "is_log_drain_enabled" INTEGER DEFAULT false NOT NULL,
+ "is_include_timestamps" INTEGER DEFAULT false NOT NULL,
+ "deleted_at" TEXT,
+ "is_gzip_enabled" INTEGER DEFAULT true NOT NULL,
+ "is_stripprefix_enabled" INTEGER DEFAULT true NOT NULL,
+ "last_online_at" TEXT DEFAULT '2026-02-11 12:51:02' NOT NULL,
+ "is_migrated" INTEGER DEFAULT false NOT NULL
+);
+
+CREATE TABLE IF NOT EXISTS "service_databases" (
+ "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+ "uuid" TEXT NOT NULL,
+ "name" TEXT NOT NULL,
+ "human_name" TEXT,
+ "description" TEXT,
+ "ports" TEXT,
+ "exposes" TEXT,
+ "status" TEXT DEFAULT 'exited' NOT NULL,
+ "service_id" INTEGER NOT NULL,
+ "created_at" TEXT,
+ "updated_at" TEXT,
+ "exclude_from_status" INTEGER DEFAULT false NOT NULL,
+ "image" TEXT,
+ "public_port" INTEGER,
+ "is_public" INTEGER DEFAULT false NOT NULL,
+ "is_log_drain_enabled" INTEGER DEFAULT false NOT NULL,
+ "is_include_timestamps" INTEGER DEFAULT false NOT NULL,
+ "deleted_at" TEXT,
+ "is_gzip_enabled" INTEGER DEFAULT true NOT NULL,
+ "is_stripprefix_enabled" INTEGER DEFAULT true NOT NULL,
+ "last_online_at" TEXT DEFAULT '2026-02-11 12:51:02' NOT NULL,
+ "is_migrated" INTEGER DEFAULT false NOT NULL,
+ "custom_type" TEXT
+);
+
+CREATE TABLE IF NOT EXISTS "services" (
+ "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+ "uuid" TEXT NOT NULL,
+ "name" TEXT NOT NULL,
+ "environment_id" INTEGER NOT NULL,
+ "created_at" TEXT,
+ "updated_at" TEXT,
+ "server_id" INTEGER,
+ "description" TEXT,
+ "docker_compose_raw" TEXT NOT NULL,
+ "docker_compose" TEXT,
+ "destination_type" TEXT,
+ "destination_id" INTEGER,
+ "deleted_at" TEXT,
+ "connect_to_docker_network" INTEGER DEFAULT false NOT NULL,
+ "config_hash" TEXT,
+ "service_type" TEXT,
+ "is_container_label_escape_enabled" INTEGER DEFAULT true NOT NULL,
+ "compose_parsing_version" TEXT DEFAULT '2' NOT NULL
+);
+
+CREATE TABLE IF NOT EXISTS "sessions" (
+ "id" TEXT NOT NULL,
+ "user_id" INTEGER,
+ "ip_address" TEXT,
+ "user_agent" TEXT,
+ "payload" TEXT NOT NULL,
+ "last_activity" INTEGER NOT NULL
+);
+
+CREATE TABLE IF NOT EXISTS "shared_environment_variables" (
+ "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+ "key" TEXT NOT NULL,
+ "value" TEXT NOT NULL,
+ "is_shown_once" INTEGER DEFAULT false NOT NULL,
+ "type" TEXT DEFAULT 'team' NOT NULL,
+ "team_id" INTEGER NOT NULL,
+ "project_id" INTEGER,
+ "environment_id" INTEGER,
+ "created_at" TEXT,
+ "updated_at" TEXT,
+ "is_multiline" INTEGER DEFAULT false NOT NULL,
+ "version" TEXT DEFAULT '4.0.0-beta.239' NOT NULL,
+ "is_literal" INTEGER DEFAULT false NOT NULL
+);
+
+CREATE TABLE IF NOT EXISTS "slack_notification_settings" (
+ "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+ "team_id" INTEGER NOT NULL,
+ "slack_enabled" INTEGER DEFAULT false NOT NULL,
+ "slack_webhook_url" TEXT,
+ "deployment_success_slack_notifications" INTEGER DEFAULT false NOT NULL,
+ "deployment_failure_slack_notifications" INTEGER DEFAULT true NOT NULL,
+ "status_change_slack_notifications" INTEGER DEFAULT false NOT NULL,
+ "backup_success_slack_notifications" INTEGER DEFAULT false NOT NULL,
+ "backup_failure_slack_notifications" INTEGER DEFAULT true NOT NULL,
+ "scheduled_task_success_slack_notifications" INTEGER DEFAULT false NOT NULL,
+ "scheduled_task_failure_slack_notifications" INTEGER DEFAULT true NOT NULL,
+ "docker_cleanup_success_slack_notifications" INTEGER DEFAULT false NOT NULL,
+ "docker_cleanup_failure_slack_notifications" INTEGER DEFAULT true NOT NULL,
+ "server_disk_usage_slack_notifications" INTEGER DEFAULT true NOT NULL,
+ "server_reachable_slack_notifications" INTEGER DEFAULT false NOT NULL,
+ "server_unreachable_slack_notifications" INTEGER DEFAULT true NOT NULL,
+ "server_patch_slack_notifications" INTEGER DEFAULT true NOT NULL,
+ "traefik_outdated_slack_notifications" INTEGER DEFAULT true NOT NULL
+);
+
+CREATE TABLE IF NOT EXISTS "ssl_certificates" (
+ "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+ "ssl_certificate" TEXT NOT NULL,
+ "ssl_private_key" TEXT NOT NULL,
+ "configuration_dir" TEXT,
+ "mount_path" TEXT,
+ "resource_type" TEXT,
+ "resource_id" INTEGER,
+ "server_id" INTEGER NOT NULL,
+ "common_name" TEXT NOT NULL,
+ "subject_alternative_names" TEXT,
+ "valid_until" TEXT NOT NULL,
+ "is_ca_certificate" INTEGER DEFAULT false NOT NULL,
+ "created_at" TEXT,
+ "updated_at" TEXT
+);
+
+CREATE TABLE IF NOT EXISTS "standalone_clickhouses" (
+ "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+ "uuid" TEXT NOT NULL,
+ "name" TEXT NOT NULL,
+ "description" TEXT,
+ "clickhouse_admin_user" TEXT DEFAULT 'default' NOT NULL,
+ "clickhouse_admin_password" TEXT NOT NULL,
+ "is_log_drain_enabled" INTEGER DEFAULT false NOT NULL,
+ "is_include_timestamps" INTEGER DEFAULT false NOT NULL,
+ "deleted_at" TEXT,
+ "status" TEXT DEFAULT 'exited' NOT NULL,
+ "image" TEXT DEFAULT 'clickhouse/clickhouse-server:25.11' NOT NULL,
+ "is_public" INTEGER DEFAULT false NOT NULL,
+ "public_port" INTEGER,
+ "ports_mappings" TEXT,
+ "limits_memory" TEXT DEFAULT '0' NOT NULL,
+ "limits_memory_swap" TEXT DEFAULT '0' NOT NULL,
+ "limits_memory_swappiness" INTEGER DEFAULT 60 NOT NULL,
+ "limits_memory_reservation" TEXT DEFAULT '0' NOT NULL,
+ "limits_cpus" TEXT DEFAULT '0' NOT NULL,
+ "limits_cpuset" TEXT,
+ "limits_cpu_shares" INTEGER DEFAULT 1024 NOT NULL,
+ "started_at" TEXT,
+ "destination_type" TEXT NOT NULL,
+ "destination_id" INTEGER NOT NULL,
+ "environment_id" INTEGER,
+ "created_at" TEXT,
+ "updated_at" TEXT,
+ "config_hash" TEXT,
+ "custom_docker_run_options" TEXT,
+ "last_online_at" TEXT DEFAULT '2026-02-11 12:51:02' NOT NULL,
+ "clickhouse_db" TEXT DEFAULT 'default' NOT NULL,
+ "restart_count" INTEGER DEFAULT 0 NOT NULL,
+ "last_restart_at" TEXT,
+ "last_restart_type" TEXT
+);
+
+CREATE TABLE IF NOT EXISTS "standalone_dockers" (
+ "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+ "name" TEXT NOT NULL,
+ "uuid" TEXT NOT NULL,
+ "network" TEXT NOT NULL,
+ "server_id" INTEGER NOT NULL,
+ "created_at" TEXT,
+ "updated_at" TEXT
+);
+
+CREATE TABLE IF NOT EXISTS "standalone_dragonflies" (
+ "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+ "uuid" TEXT NOT NULL,
+ "name" TEXT NOT NULL,
+ "description" TEXT,
+ "dragonfly_password" TEXT NOT NULL,
+ "is_log_drain_enabled" INTEGER DEFAULT false NOT NULL,
+ "is_include_timestamps" INTEGER DEFAULT false NOT NULL,
+ "deleted_at" TEXT,
+ "status" TEXT DEFAULT 'exited' NOT NULL,
+ "image" TEXT DEFAULT 'docker.dragonflydb.io/dragonflydb/dragonfly' NOT NULL,
+ "is_public" INTEGER DEFAULT false NOT NULL,
+ "public_port" INTEGER,
+ "ports_mappings" TEXT,
+ "limits_memory" TEXT DEFAULT '0' NOT NULL,
+ "limits_memory_swap" TEXT DEFAULT '0' NOT NULL,
+ "limits_memory_swappiness" INTEGER DEFAULT 60 NOT NULL,
+ "limits_memory_reservation" TEXT DEFAULT '0' NOT NULL,
+ "limits_cpus" TEXT DEFAULT '0' NOT NULL,
+ "limits_cpuset" TEXT,
+ "limits_cpu_shares" INTEGER DEFAULT 1024 NOT NULL,
+ "started_at" TEXT,
+ "destination_type" TEXT NOT NULL,
+ "destination_id" INTEGER NOT NULL,
+ "environment_id" INTEGER,
+ "created_at" TEXT,
+ "updated_at" TEXT,
+ "config_hash" TEXT,
+ "custom_docker_run_options" TEXT,
+ "last_online_at" TEXT DEFAULT '2026-02-11 12:51:02' NOT NULL,
+ "enable_ssl" INTEGER DEFAULT false NOT NULL,
+ "restart_count" INTEGER DEFAULT 0 NOT NULL,
+ "last_restart_at" TEXT,
+ "last_restart_type" TEXT
+);
+
+CREATE TABLE IF NOT EXISTS "standalone_keydbs" (
+ "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+ "uuid" TEXT NOT NULL,
+ "name" TEXT NOT NULL,
+ "description" TEXT,
+ "keydb_password" TEXT NOT NULL,
+ "keydb_conf" TEXT,
+ "is_log_drain_enabled" INTEGER DEFAULT false NOT NULL,
+ "is_include_timestamps" INTEGER DEFAULT false NOT NULL,
+ "deleted_at" TEXT,
+ "status" TEXT DEFAULT 'exited' NOT NULL,
+ "image" TEXT DEFAULT 'eqalpha/keydb:latest' NOT NULL,
+ "is_public" INTEGER DEFAULT false NOT NULL,
+ "public_port" INTEGER,
+ "ports_mappings" TEXT,
+ "limits_memory" TEXT DEFAULT '0' NOT NULL,
+ "limits_memory_swap" TEXT DEFAULT '0' NOT NULL,
+ "limits_memory_swappiness" INTEGER DEFAULT 60 NOT NULL,
+ "limits_memory_reservation" TEXT DEFAULT '0' NOT NULL,
+ "limits_cpus" TEXT DEFAULT '0' NOT NULL,
+ "limits_cpuset" TEXT,
+ "limits_cpu_shares" INTEGER DEFAULT 1024 NOT NULL,
+ "started_at" TEXT,
+ "destination_type" TEXT NOT NULL,
+ "destination_id" INTEGER NOT NULL,
+ "environment_id" INTEGER,
+ "created_at" TEXT,
+ "updated_at" TEXT,
+ "config_hash" TEXT,
+ "custom_docker_run_options" TEXT,
+ "last_online_at" TEXT DEFAULT '2026-02-11 12:51:02' NOT NULL,
+ "enable_ssl" INTEGER DEFAULT false NOT NULL,
+ "restart_count" INTEGER DEFAULT 0 NOT NULL,
+ "last_restart_at" TEXT,
+ "last_restart_type" TEXT
+);
+
+CREATE TABLE IF NOT EXISTS "standalone_mariadbs" (
+ "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+ "uuid" TEXT NOT NULL,
+ "name" TEXT NOT NULL,
+ "description" TEXT,
+ "mariadb_root_password" TEXT NOT NULL,
+ "mariadb_user" TEXT DEFAULT 'mariadb' NOT NULL,
+ "mariadb_password" TEXT NOT NULL,
+ "mariadb_database" TEXT DEFAULT 'default' NOT NULL,
+ "mariadb_conf" TEXT,
+ "status" TEXT DEFAULT 'exited' NOT NULL,
+ "image" TEXT DEFAULT 'mariadb:11' NOT NULL,
+ "is_public" INTEGER DEFAULT false NOT NULL,
+ "public_port" INTEGER,
+ "ports_mappings" TEXT,
+ "limits_memory" TEXT DEFAULT '0' NOT NULL,
+ "limits_memory_swap" TEXT DEFAULT '0' NOT NULL,
+ "limits_memory_swappiness" INTEGER DEFAULT 60 NOT NULL,
+ "limits_memory_reservation" TEXT DEFAULT '0' NOT NULL,
+ "limits_cpus" TEXT DEFAULT '0' NOT NULL,
+ "limits_cpuset" TEXT,
+ "limits_cpu_shares" INTEGER DEFAULT 1024 NOT NULL,
+ "started_at" TEXT,
+ "destination_type" TEXT NOT NULL,
+ "destination_id" INTEGER NOT NULL,
+ "environment_id" INTEGER,
+ "created_at" TEXT,
+ "updated_at" TEXT,
+ "is_log_drain_enabled" INTEGER DEFAULT false NOT NULL,
+ "deleted_at" TEXT,
+ "config_hash" TEXT,
+ "custom_docker_run_options" TEXT,
+ "last_online_at" TEXT DEFAULT '2026-02-11 12:51:02' NOT NULL,
+ "enable_ssl" INTEGER DEFAULT false NOT NULL,
+ "restart_count" INTEGER DEFAULT 0 NOT NULL,
+ "last_restart_at" TEXT,
+ "last_restart_type" TEXT
+);
+
+CREATE TABLE IF NOT EXISTS "standalone_mongodbs" (
+ "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+ "uuid" TEXT NOT NULL,
+ "name" TEXT NOT NULL,
+ "description" TEXT,
+ "mongo_conf" TEXT,
+ "mongo_initdb_root_username" TEXT DEFAULT 'root' NOT NULL,
+ "mongo_initdb_root_password" TEXT NOT NULL,
+ "mongo_initdb_database" TEXT DEFAULT 'default' NOT NULL,
+ "status" TEXT DEFAULT 'exited' NOT NULL,
+ "image" TEXT DEFAULT 'mongo:7' NOT NULL,
+ "is_public" INTEGER DEFAULT false NOT NULL,
+ "public_port" INTEGER,
+ "ports_mappings" TEXT,
+ "limits_memory" TEXT DEFAULT '0' NOT NULL,
+ "limits_memory_swap" TEXT DEFAULT '0' NOT NULL,
+ "limits_memory_swappiness" INTEGER DEFAULT 60 NOT NULL,
+ "limits_memory_reservation" TEXT DEFAULT '0' NOT NULL,
+ "limits_cpus" TEXT DEFAULT '0' NOT NULL,
+ "limits_cpuset" TEXT,
+ "limits_cpu_shares" INTEGER DEFAULT 1024 NOT NULL,
+ "started_at" TEXT,
+ "destination_type" TEXT NOT NULL,
+ "destination_id" INTEGER NOT NULL,
+ "environment_id" INTEGER,
+ "created_at" TEXT,
+ "updated_at" TEXT,
+ "is_log_drain_enabled" INTEGER DEFAULT false NOT NULL,
+ "is_include_timestamps" INTEGER DEFAULT false NOT NULL,
+ "deleted_at" TEXT,
+ "config_hash" TEXT,
+ "custom_docker_run_options" TEXT,
+ "last_online_at" TEXT DEFAULT '2026-02-11 12:51:02' NOT NULL,
+ "enable_ssl" INTEGER DEFAULT false NOT NULL,
+ "ssl_mode" TEXT DEFAULT 'require' NOT NULL,
+ "restart_count" INTEGER DEFAULT 0 NOT NULL,
+ "last_restart_at" TEXT,
+ "last_restart_type" TEXT
+);
+
+CREATE TABLE IF NOT EXISTS "standalone_mysqls" (
+ "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+ "uuid" TEXT NOT NULL,
+ "name" TEXT NOT NULL,
+ "description" TEXT,
+ "mysql_root_password" TEXT NOT NULL,
+ "mysql_user" TEXT DEFAULT 'mysql' NOT NULL,
+ "mysql_password" TEXT NOT NULL,
+ "mysql_database" TEXT DEFAULT 'default' NOT NULL,
+ "mysql_conf" TEXT,
+ "status" TEXT DEFAULT 'exited' NOT NULL,
+ "image" TEXT DEFAULT 'mysql:8' NOT NULL,
+ "is_public" INTEGER DEFAULT false NOT NULL,
+ "public_port" INTEGER,
+ "ports_mappings" TEXT,
+ "limits_memory" TEXT DEFAULT '0' NOT NULL,
+ "limits_memory_swap" TEXT DEFAULT '0' NOT NULL,
+ "limits_memory_swappiness" INTEGER DEFAULT 60 NOT NULL,
+ "limits_memory_reservation" TEXT DEFAULT '0' NOT NULL,
+ "limits_cpus" TEXT DEFAULT '0' NOT NULL,
+ "limits_cpuset" TEXT,
+ "limits_cpu_shares" INTEGER DEFAULT 1024 NOT NULL,
+ "started_at" TEXT,
+ "destination_type" TEXT NOT NULL,
+ "destination_id" INTEGER NOT NULL,
+ "environment_id" INTEGER,
+ "created_at" TEXT,
+ "updated_at" TEXT,
+ "is_log_drain_enabled" INTEGER DEFAULT false NOT NULL,
+ "is_include_timestamps" INTEGER DEFAULT false NOT NULL,
+ "deleted_at" TEXT,
+ "config_hash" TEXT,
+ "custom_docker_run_options" TEXT,
+ "last_online_at" TEXT DEFAULT '2026-02-11 12:51:02' NOT NULL,
+ "enable_ssl" INTEGER DEFAULT false NOT NULL,
+ "ssl_mode" TEXT DEFAULT 'REQUIRED' NOT NULL,
+ "restart_count" INTEGER DEFAULT 0 NOT NULL,
+ "last_restart_at" TEXT,
+ "last_restart_type" TEXT
+);
+
+CREATE TABLE IF NOT EXISTS "standalone_postgresqls" (
+ "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+ "uuid" TEXT NOT NULL,
+ "name" TEXT NOT NULL,
+ "description" TEXT,
+ "postgres_user" TEXT DEFAULT 'postgres' NOT NULL,
+ "postgres_password" TEXT NOT NULL,
+ "postgres_db" TEXT DEFAULT 'postgres' NOT NULL,
+ "postgres_initdb_args" TEXT,
+ "postgres_host_auth_method" TEXT,
+ "init_scripts" TEXT,
+ "status" TEXT DEFAULT 'exited' NOT NULL,
+ "image" TEXT DEFAULT 'postgres:16-alpine' NOT NULL,
+ "is_public" INTEGER DEFAULT false NOT NULL,
+ "public_port" INTEGER,
+ "ports_mappings" TEXT,
+ "limits_memory" TEXT DEFAULT '0' NOT NULL,
+ "limits_memory_swap" TEXT DEFAULT '0' NOT NULL,
+ "limits_memory_swappiness" INTEGER DEFAULT 60 NOT NULL,
+ "limits_memory_reservation" TEXT DEFAULT '0' NOT NULL,
+ "limits_cpus" TEXT DEFAULT '0' NOT NULL,
+ "limits_cpuset" TEXT,
+ "limits_cpu_shares" INTEGER DEFAULT 1024 NOT NULL,
+ "started_at" TEXT,
+ "destination_type" TEXT NOT NULL,
+ "destination_id" INTEGER NOT NULL,
+ "environment_id" INTEGER,
+ "created_at" TEXT,
+ "updated_at" TEXT,
+ "postgres_conf" TEXT,
+ "is_log_drain_enabled" INTEGER DEFAULT false NOT NULL,
+ "is_include_timestamps" INTEGER DEFAULT false NOT NULL,
+ "deleted_at" TEXT,
+ "config_hash" TEXT,
+ "custom_docker_run_options" TEXT,
+ "last_online_at" TEXT DEFAULT '2026-02-11 12:51:02' NOT NULL,
+ "enable_ssl" INTEGER DEFAULT false NOT NULL,
+ "ssl_mode" TEXT DEFAULT 'require' NOT NULL,
+ "restart_count" INTEGER DEFAULT 0 NOT NULL,
+ "last_restart_at" TEXT,
+ "last_restart_type" TEXT
+);
+
+CREATE TABLE IF NOT EXISTS "standalone_redis" (
+ "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+ "uuid" TEXT NOT NULL,
+ "name" TEXT NOT NULL,
+ "description" TEXT,
+ "redis_conf" TEXT,
+ "status" TEXT DEFAULT 'exited' NOT NULL,
+ "image" TEXT DEFAULT 'redis:7.2' NOT NULL,
+ "is_public" INTEGER DEFAULT false NOT NULL,
+ "public_port" INTEGER,
+ "ports_mappings" TEXT,
+ "limits_memory" TEXT DEFAULT '0' NOT NULL,
+ "limits_memory_swap" TEXT DEFAULT '0' NOT NULL,
+ "limits_memory_swappiness" INTEGER DEFAULT 60 NOT NULL,
+ "limits_memory_reservation" TEXT DEFAULT '0' NOT NULL,
+ "limits_cpus" TEXT DEFAULT '0' NOT NULL,
+ "limits_cpuset" TEXT,
+ "limits_cpu_shares" INTEGER DEFAULT 1024 NOT NULL,
+ "started_at" TEXT,
+ "destination_type" TEXT NOT NULL,
+ "destination_id" INTEGER NOT NULL,
+ "environment_id" INTEGER,
+ "created_at" TEXT,
+ "updated_at" TEXT,
+ "is_log_drain_enabled" INTEGER DEFAULT false NOT NULL,
+ "is_include_timestamps" INTEGER DEFAULT false NOT NULL,
+ "deleted_at" TEXT,
+ "config_hash" TEXT,
+ "custom_docker_run_options" TEXT,
+ "last_online_at" TEXT DEFAULT '2026-02-11 12:51:02' NOT NULL,
+ "enable_ssl" INTEGER DEFAULT false NOT NULL,
+ "restart_count" INTEGER DEFAULT 0 NOT NULL,
+ "last_restart_at" TEXT,
+ "last_restart_type" TEXT
+);
+
+CREATE TABLE IF NOT EXISTS "subscriptions" (
+ "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+ "team_id" INTEGER NOT NULL,
+ "created_at" TEXT,
+ "updated_at" TEXT,
+ "stripe_invoice_paid" INTEGER DEFAULT false NOT NULL,
+ "stripe_subscription_id" TEXT,
+ "stripe_customer_id" TEXT,
+ "stripe_cancel_at_period_end" INTEGER DEFAULT false NOT NULL,
+ "stripe_plan_id" TEXT,
+ "stripe_feedback" TEXT,
+ "stripe_comment" TEXT,
+ "stripe_trial_already_ended" INTEGER DEFAULT false NOT NULL,
+ "stripe_past_due" INTEGER DEFAULT false NOT NULL
+);
+
+CREATE TABLE IF NOT EXISTS "swarm_dockers" (
+ "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+ "name" TEXT NOT NULL,
+ "uuid" TEXT NOT NULL,
+ "server_id" INTEGER NOT NULL,
+ "created_at" TEXT,
+ "updated_at" TEXT,
+ "network" TEXT NOT NULL
+);
+
+CREATE TABLE IF NOT EXISTS "taggables" (
+ "tag_id" INTEGER NOT NULL,
+ "taggable_id" INTEGER NOT NULL,
+ "taggable_type" TEXT NOT NULL
+);
+
+CREATE TABLE IF NOT EXISTS "tags" (
+ "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+ "uuid" TEXT NOT NULL,
+ "name" TEXT NOT NULL,
+ "team_id" INTEGER,
+ "created_at" TEXT,
+ "updated_at" TEXT
+);
+
+CREATE TABLE IF NOT EXISTS "team_invitations" (
+ "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+ "uuid" TEXT NOT NULL,
+ "team_id" INTEGER NOT NULL,
+ "email" TEXT NOT NULL,
+ "role" TEXT DEFAULT 'member' NOT NULL,
+ "link" TEXT NOT NULL,
+ "via" TEXT DEFAULT 'link' NOT NULL,
+ "created_at" TEXT,
+ "updated_at" TEXT
+);
+
+CREATE TABLE IF NOT EXISTS "team_user" (
+ "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+ "team_id" INTEGER NOT NULL,
+ "user_id" INTEGER NOT NULL,
+ "role" TEXT DEFAULT 'member' NOT NULL,
+ "created_at" TEXT,
+ "updated_at" TEXT
+);
+
+CREATE TABLE IF NOT EXISTS "teams" (
+ "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+ "name" TEXT NOT NULL,
+ "description" TEXT,
+ "personal_team" INTEGER DEFAULT false NOT NULL,
+ "created_at" TEXT,
+ "updated_at" TEXT,
+ "show_boarding" INTEGER DEFAULT false NOT NULL,
+ "custom_server_limit" INTEGER
+);
+
+CREATE TABLE IF NOT EXISTS "telegram_notification_settings" (
+ "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+ "team_id" INTEGER NOT NULL,
+ "telegram_enabled" INTEGER DEFAULT false NOT NULL,
+ "telegram_token" TEXT,
+ "telegram_chat_id" TEXT,
+ "deployment_success_telegram_notifications" INTEGER DEFAULT false NOT NULL,
+ "deployment_failure_telegram_notifications" INTEGER DEFAULT true NOT NULL,
+ "status_change_telegram_notifications" INTEGER DEFAULT false NOT NULL,
+ "backup_success_telegram_notifications" INTEGER DEFAULT false NOT NULL,
+ "backup_failure_telegram_notifications" INTEGER DEFAULT true NOT NULL,
+ "scheduled_task_success_telegram_notifications" INTEGER DEFAULT false NOT NULL,
+ "scheduled_task_failure_telegram_notifications" INTEGER DEFAULT true NOT NULL,
+ "docker_cleanup_success_telegram_notifications" INTEGER DEFAULT false NOT NULL,
+ "docker_cleanup_failure_telegram_notifications" INTEGER DEFAULT true NOT NULL,
+ "server_disk_usage_telegram_notifications" INTEGER DEFAULT true NOT NULL,
+ "server_reachable_telegram_notifications" INTEGER DEFAULT false NOT NULL,
+ "server_unreachable_telegram_notifications" INTEGER DEFAULT true NOT NULL,
+ "telegram_notifications_deployment_success_thread_id" TEXT,
+ "telegram_notifications_deployment_failure_thread_id" TEXT,
+ "telegram_notifications_status_change_thread_id" TEXT,
+ "telegram_notifications_backup_success_thread_id" TEXT,
+ "telegram_notifications_backup_failure_thread_id" TEXT,
+ "telegram_notifications_scheduled_task_success_thread_id" TEXT,
+ "telegram_notifications_scheduled_task_failure_thread_id" TEXT,
+ "telegram_notifications_docker_cleanup_success_thread_id" TEXT,
+ "telegram_notifications_docker_cleanup_failure_thread_id" TEXT,
+ "telegram_notifications_server_disk_usage_thread_id" TEXT,
+ "telegram_notifications_server_reachable_thread_id" TEXT,
+ "telegram_notifications_server_unreachable_thread_id" TEXT,
+ "server_patch_telegram_notifications" INTEGER DEFAULT true NOT NULL,
+ "telegram_notifications_server_patch_thread_id" TEXT,
+ "telegram_notifications_traefik_outdated_thread_id" TEXT,
+ "traefik_outdated_telegram_notifications" INTEGER DEFAULT true NOT NULL
+);
+
+CREATE TABLE IF NOT EXISTS "telescope_entries" (
+ "sequence" INTEGER NOT NULL,
+ "uuid" TEXT NOT NULL,
+ "batch_id" TEXT NOT NULL,
+ "family_hash" TEXT,
+ "should_display_on_index" INTEGER DEFAULT true NOT NULL,
+ "type" TEXT NOT NULL,
+ "content" TEXT NOT NULL,
+ "created_at" TEXT
+);
+
+CREATE TABLE IF NOT EXISTS "telescope_entries_tags" (
+ "entry_uuid" TEXT NOT NULL,
+ "tag" TEXT NOT NULL
+);
+
+CREATE TABLE IF NOT EXISTS "telescope_monitoring" (
+ "tag" TEXT NOT NULL
+);
+
+CREATE TABLE IF NOT EXISTS "user_changelog_reads" (
+ "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+ "user_id" INTEGER NOT NULL,
+ "release_tag" TEXT NOT NULL,
+ "read_at" TEXT NOT NULL,
+ "created_at" TEXT,
+ "updated_at" TEXT
+);
+
+CREATE TABLE IF NOT EXISTS "users" (
+ "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+ "name" TEXT DEFAULT 'Anonymous' NOT NULL,
+ "email" TEXT NOT NULL,
+ "email_verified_at" TEXT,
+ "password" TEXT,
+ "remember_token" TEXT,
+ "created_at" TEXT,
+ "updated_at" TEXT,
+ "two_factor_secret" TEXT,
+ "two_factor_recovery_codes" TEXT,
+ "two_factor_confirmed_at" TEXT,
+ "force_password_reset" INTEGER DEFAULT false NOT NULL,
+ "marketing_emails" INTEGER DEFAULT true NOT NULL,
+ "pending_email" TEXT,
+ "email_change_code" TEXT,
+ "email_change_code_expires_at" TEXT
+);
+
+CREATE TABLE IF NOT EXISTS "webhook_notification_settings" (
+ "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+ "team_id" INTEGER NOT NULL,
+ "webhook_enabled" INTEGER DEFAULT false NOT NULL,
+ "webhook_url" TEXT,
+ "deployment_success_webhook_notifications" INTEGER DEFAULT false NOT NULL,
+ "deployment_failure_webhook_notifications" INTEGER DEFAULT true NOT NULL,
+ "status_change_webhook_notifications" INTEGER DEFAULT false NOT NULL,
+ "backup_success_webhook_notifications" INTEGER DEFAULT false NOT NULL,
+ "backup_failure_webhook_notifications" INTEGER DEFAULT true NOT NULL,
+ "scheduled_task_success_webhook_notifications" INTEGER DEFAULT false NOT NULL,
+ "scheduled_task_failure_webhook_notifications" INTEGER DEFAULT true NOT NULL,
+ "docker_cleanup_success_webhook_notifications" INTEGER DEFAULT false NOT NULL,
+ "docker_cleanup_failure_webhook_notifications" INTEGER DEFAULT true NOT NULL,
+ "server_disk_usage_webhook_notifications" INTEGER DEFAULT true NOT NULL,
+ "server_reachable_webhook_notifications" INTEGER DEFAULT false NOT NULL,
+ "server_unreachable_webhook_notifications" INTEGER DEFAULT true NOT NULL,
+ "server_patch_webhook_notifications" INTEGER DEFAULT false NOT NULL,
+ "traefik_outdated_webhook_notifications" INTEGER DEFAULT true NOT NULL
+);
+
+CREATE INDEX IF NOT EXISTS "activity_log_log_name_index" ON "activity_log" (log_name);
+CREATE INDEX IF NOT EXISTS "causer" ON "activity_log" (causer_type, causer_id);
+CREATE INDEX IF NOT EXISTS "subject" ON "activity_log" (subject_type, subject_id);
+CREATE UNIQUE INDEX IF NOT EXISTS "application_deployment_queues_deployment_uuid_unique" ON "application_deployment_queues" (deployment_uuid);
+CREATE INDEX IF NOT EXISTS "idx_deployment_queues_app_status_pr_created" ON "application_deployment_queues" (application_id, status, pull_request_id, created_at);
+CREATE INDEX IF NOT EXISTS "idx_deployment_queues_status_server" ON "application_deployment_queues" (status, server_id);
+CREATE UNIQUE INDEX IF NOT EXISTS "application_previews_fqdn_unique" ON "application_previews" (fqdn);
+CREATE UNIQUE INDEX IF NOT EXISTS "application_previews_uuid_unique" ON "application_previews" (uuid);
+CREATE INDEX IF NOT EXISTS "applications_destination_type_destination_id_index" ON "applications" (destination_type, destination_id);
+CREATE INDEX IF NOT EXISTS "applications_source_type_source_id_index" ON "applications" (source_type, source_id);
+CREATE UNIQUE INDEX IF NOT EXISTS "applications_uuid_unique" ON "applications" (uuid);
+CREATE INDEX IF NOT EXISTS "idx_cloud_init_scripts_team_id" ON "cloud_init_scripts" (team_id);
+CREATE INDEX IF NOT EXISTS "cloud_provider_tokens_team_id_provider_index" ON "cloud_provider_tokens" (team_id, provider);
+CREATE UNIQUE INDEX IF NOT EXISTS "cloud_provider_tokens_uuid_unique" ON "cloud_provider_tokens" (uuid);
+CREATE INDEX IF NOT EXISTS "idx_cloud_provider_tokens_team_id" ON "cloud_provider_tokens" (team_id);
+CREATE UNIQUE INDEX IF NOT EXISTS "discord_notification_settings_team_id_unique" ON "discord_notification_settings" (team_id);
+CREATE UNIQUE INDEX IF NOT EXISTS "docker_cleanup_executions_uuid_unique" ON "docker_cleanup_executions" (uuid);
+CREATE UNIQUE INDEX IF NOT EXISTS "email_notification_settings_team_id_unique" ON "email_notification_settings" (team_id);
+CREATE INDEX IF NOT EXISTS "environment_variables_resourceable_type_resourceable_id_index" ON "environment_variables" (resourceable_type, resourceable_id);
+CREATE UNIQUE INDEX IF NOT EXISTS "environments_name_project_id_unique" ON "environments" (name, project_id);
+CREATE UNIQUE INDEX IF NOT EXISTS "environments_uuid_unique" ON "environments" (uuid);
+CREATE INDEX IF NOT EXISTS "idx_environments_project_id" ON "environments" (project_id);
+CREATE UNIQUE INDEX IF NOT EXISTS "failed_jobs_uuid_unique" ON "failed_jobs" (uuid);
+CREATE UNIQUE INDEX IF NOT EXISTS "github_apps_uuid_unique" ON "github_apps" (uuid);
+CREATE UNIQUE INDEX IF NOT EXISTS "gitlab_apps_uuid_unique" ON "gitlab_apps" (uuid);
+CREATE UNIQUE INDEX IF NOT EXISTS "local_file_volumes_mount_path_resource_id_resource_type_unique" ON "local_file_volumes" (mount_path, resource_id, resource_type);
+CREATE INDEX IF NOT EXISTS "local_file_volumes_resource_type_resource_id_index" ON "local_file_volumes" (resource_type, resource_id);
+CREATE UNIQUE INDEX IF NOT EXISTS "local_persistent_volumes_name_resource_id_resource_type_unique" ON "local_persistent_volumes" (name, resource_id, resource_type);
+CREATE INDEX IF NOT EXISTS "local_persistent_volumes_resource_type_resource_id_index" ON "local_persistent_volumes" (resource_type, resource_id);
+CREATE UNIQUE INDEX IF NOT EXISTS "oauth_settings_provider_unique" ON "oauth_settings" (provider);
+CREATE UNIQUE INDEX IF NOT EXISTS "personal_access_tokens_token_unique" ON "personal_access_tokens" (token);
+CREATE INDEX IF NOT EXISTS "personal_access_tokens_tokenable_type_tokenable_id_index" ON "personal_access_tokens" (tokenable_type, tokenable_id);
+CREATE INDEX IF NOT EXISTS "idx_private_keys_team_id" ON "private_keys" (team_id);
+CREATE UNIQUE INDEX IF NOT EXISTS "private_keys_uuid_unique" ON "private_keys" (uuid);
+CREATE INDEX IF NOT EXISTS "idx_projects_team_id" ON "projects" (team_id);
+CREATE UNIQUE INDEX IF NOT EXISTS "projects_uuid_unique" ON "projects" (uuid);
+CREATE UNIQUE INDEX IF NOT EXISTS "pushover_notification_settings_team_id_unique" ON "pushover_notification_settings" (team_id);
+CREATE UNIQUE INDEX IF NOT EXISTS "s3_storages_uuid_unique" ON "s3_storages" (uuid);
+CREATE UNIQUE INDEX IF NOT EXISTS "scheduled_database_backup_executions_uuid_unique" ON "scheduled_database_backup_executions" (uuid);
+CREATE INDEX IF NOT EXISTS "scheduled_db_backup_executions_backup_id_created_at_index" ON "scheduled_database_backup_executions" (scheduled_database_backup_id, created_at);
+CREATE INDEX IF NOT EXISTS "scheduled_database_backups_database_type_database_id_index" ON "scheduled_database_backups" (database_type, database_id);
+CREATE UNIQUE INDEX IF NOT EXISTS "scheduled_database_backups_uuid_unique" ON "scheduled_database_backups" (uuid);
+CREATE INDEX IF NOT EXISTS "scheduled_task_executions_task_id_created_at_index" ON "scheduled_task_executions" (scheduled_task_id, created_at);
+CREATE UNIQUE INDEX IF NOT EXISTS "scheduled_task_executions_uuid_unique" ON "scheduled_task_executions" (uuid);
+CREATE UNIQUE INDEX IF NOT EXISTS "scheduled_tasks_uuid_unique" ON "scheduled_tasks" (uuid);
+CREATE INDEX IF NOT EXISTS "idx_servers_team_id" ON "servers" (team_id);
+CREATE UNIQUE INDEX IF NOT EXISTS "servers_uuid_unique" ON "servers" (uuid);
+CREATE UNIQUE INDEX IF NOT EXISTS "service_applications_uuid_unique" ON "service_applications" (uuid);
+CREATE UNIQUE INDEX IF NOT EXISTS "service_databases_uuid_unique" ON "service_databases" (uuid);
+CREATE INDEX IF NOT EXISTS "services_destination_type_destination_id_index" ON "services" (destination_type, destination_id);
+CREATE UNIQUE INDEX IF NOT EXISTS "services_uuid_unique" ON "services" (uuid);
+CREATE INDEX IF NOT EXISTS "sessions_last_activity_index" ON "sessions" (last_activity);
+CREATE INDEX IF NOT EXISTS "sessions_user_id_index" ON "sessions" (user_id);
+CREATE UNIQUE INDEX IF NOT EXISTS "shared_environment_variables_key_environment_id_team_id_unique" ON "shared_environment_variables" (key, environment_id, team_id);
+CREATE UNIQUE INDEX IF NOT EXISTS "shared_environment_variables_key_project_id_team_id_unique" ON "shared_environment_variables" (key, project_id, team_id);
+CREATE UNIQUE INDEX IF NOT EXISTS "slack_notification_settings_team_id_unique" ON "slack_notification_settings" (team_id);
+CREATE INDEX IF NOT EXISTS "standalone_clickhouses_destination_type_destination_id_index" ON "standalone_clickhouses" (destination_type, destination_id);
+CREATE UNIQUE INDEX IF NOT EXISTS "standalone_clickhouses_uuid_unique" ON "standalone_clickhouses" (uuid);
+CREATE UNIQUE INDEX IF NOT EXISTS "standalone_dockers_server_id_network_unique" ON "standalone_dockers" (server_id, network);
+CREATE UNIQUE INDEX IF NOT EXISTS "standalone_dockers_uuid_unique" ON "standalone_dockers" (uuid);
+CREATE INDEX IF NOT EXISTS "standalone_dragonflies_destination_type_destination_id_index" ON "standalone_dragonflies" (destination_type, destination_id);
+CREATE UNIQUE INDEX IF NOT EXISTS "standalone_dragonflies_uuid_unique" ON "standalone_dragonflies" (uuid);
+CREATE INDEX IF NOT EXISTS "standalone_keydbs_destination_type_destination_id_index" ON "standalone_keydbs" (destination_type, destination_id);
+CREATE UNIQUE INDEX IF NOT EXISTS "standalone_keydbs_uuid_unique" ON "standalone_keydbs" (uuid);
+CREATE INDEX IF NOT EXISTS "standalone_mariadbs_destination_type_destination_id_index" ON "standalone_mariadbs" (destination_type, destination_id);
+CREATE UNIQUE INDEX IF NOT EXISTS "standalone_mariadbs_uuid_unique" ON "standalone_mariadbs" (uuid);
+CREATE INDEX IF NOT EXISTS "standalone_mongodbs_destination_type_destination_id_index" ON "standalone_mongodbs" (destination_type, destination_id);
+CREATE UNIQUE INDEX IF NOT EXISTS "standalone_mongodbs_uuid_unique" ON "standalone_mongodbs" (uuid);
+CREATE INDEX IF NOT EXISTS "standalone_mysqls_destination_type_destination_id_index" ON "standalone_mysqls" (destination_type, destination_id);
+CREATE UNIQUE INDEX IF NOT EXISTS "standalone_mysqls_uuid_unique" ON "standalone_mysqls" (uuid);
+CREATE INDEX IF NOT EXISTS "standalone_postgresqls_destination_type_destination_id_index" ON "standalone_postgresqls" (destination_type, destination_id);
+CREATE UNIQUE INDEX IF NOT EXISTS "standalone_postgresqls_uuid_unique" ON "standalone_postgresqls" (uuid);
+CREATE INDEX IF NOT EXISTS "standalone_redis_destination_type_destination_id_index" ON "standalone_redis" (destination_type, destination_id);
+CREATE UNIQUE INDEX IF NOT EXISTS "standalone_redis_uuid_unique" ON "standalone_redis" (uuid);
+CREATE INDEX IF NOT EXISTS "idx_subscriptions_team_id" ON "subscriptions" (team_id);
+CREATE UNIQUE INDEX IF NOT EXISTS "swarm_dockers_server_id_network_unique" ON "swarm_dockers" (server_id, network);
+CREATE UNIQUE INDEX IF NOT EXISTS "swarm_dockers_uuid_unique" ON "swarm_dockers" (uuid);
+CREATE UNIQUE INDEX IF NOT EXISTS "taggable_unique" ON "taggables" (tag_id, taggable_id, taggable_type);
+CREATE UNIQUE INDEX IF NOT EXISTS "tags_uuid_unique" ON "tags" (uuid);
+CREATE UNIQUE INDEX IF NOT EXISTS "team_invitations_team_id_email_unique" ON "team_invitations" (team_id, email);
+CREATE UNIQUE INDEX IF NOT EXISTS "team_invitations_uuid_unique" ON "team_invitations" (uuid);
+CREATE UNIQUE INDEX IF NOT EXISTS "team_user_team_id_user_id_unique" ON "team_user" (team_id, user_id);
+CREATE UNIQUE INDEX IF NOT EXISTS "telegram_notification_settings_team_id_unique" ON "telegram_notification_settings" (team_id);
+CREATE INDEX IF NOT EXISTS "telescope_entries_batch_id_index" ON "telescope_entries" (batch_id);
+CREATE INDEX IF NOT EXISTS "telescope_entries_created_at_index" ON "telescope_entries" (created_at);
+CREATE INDEX IF NOT EXISTS "telescope_entries_family_hash_index" ON "telescope_entries" (family_hash);
+CREATE INDEX IF NOT EXISTS "telescope_entries_type_should_display_on_index_index" ON "telescope_entries" (type, should_display_on_index);
+CREATE UNIQUE INDEX IF NOT EXISTS "telescope_entries_uuid_unique" ON "telescope_entries" (uuid);
+CREATE INDEX IF NOT EXISTS "telescope_entries_tags_tag_index" ON "telescope_entries_tags" (tag);
+CREATE INDEX IF NOT EXISTS "user_changelog_reads_release_tag_index" ON "user_changelog_reads" (release_tag);
+CREATE INDEX IF NOT EXISTS "user_changelog_reads_user_id_index" ON "user_changelog_reads" (user_id);
+CREATE UNIQUE INDEX IF NOT EXISTS "user_changelog_reads_user_id_release_tag_unique" ON "user_changelog_reads" (user_id, release_tag);
+CREATE UNIQUE INDEX IF NOT EXISTS "users_email_unique" ON "users" (email);
+CREATE UNIQUE INDEX IF NOT EXISTS "webhook_notification_settings_team_id_unique" ON "webhook_notification_settings" (team_id);
+
+-- Migration records
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (1, '2014_10_12_000000_create_users_table', 1);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (2, '2014_10_12_100000_create_password_reset_tokens_table', 2);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (3, '2014_10_12_200000_add_two_factor_columns_to_users_table', 3);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (4, '2018_08_08_100000_create_telescope_entries_table', 4);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (5, '2019_12_14_000001_create_personal_access_tokens_table', 5);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (6, '2023_03_20_112410_create_activity_log_table', 6);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (7, '2023_03_20_112411_add_event_column_to_activity_log_table', 7);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (8, '2023_03_20_112412_add_batch_uuid_column_to_activity_log_table', 8);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (9, '2023_03_20_112809_create_sessions_table', 9);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (10, '2023_03_20_112811_create_teams_table', 10);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (11, '2023_03_20_112812_create_team_user_table', 11);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (12, '2023_03_20_112813_create_team_invitations_table', 12);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (13, '2023_03_20_112814_create_instance_settings_table', 13);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (14, '2023_03_24_140711_create_servers_table', 14);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (15, '2023_03_24_140712_create_server_settings_table', 15);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (16, '2023_03_24_140853_create_private_keys_table', 16);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (17, '2023_03_27_075351_create_projects_table', 17);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (18, '2023_03_27_075443_create_project_settings_table', 18);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (19, '2023_03_27_075444_create_environments_table', 19);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (20, '2023_03_27_081716_create_applications_table', 20);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (21, '2023_03_27_081717_create_application_settings_table', 21);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (22, '2023_03_27_081718_create_application_previews_table', 22);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (23, '2023_03_27_083621_create_services_table', 23);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (24, '2023_03_27_085020_create_standalone_dockers_table', 24);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (25, '2023_03_27_085022_create_swarm_dockers_table', 25);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (26, '2023_03_28_062150_create_kubernetes_table', 26);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (27, '2023_03_28_083723_create_github_apps_table', 27);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (28, '2023_03_28_083726_create_gitlab_apps_table', 28);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (29, '2023_04_03_111012_create_local_persistent_volumes_table', 29);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (30, '2023_05_04_194548_create_environment_variables_table', 30);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (31, '2023_05_17_104039_create_failed_jobs_table', 31);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (32, '2023_05_24_083426_create_application_deployment_queues_table', 32);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (33, '2023_06_22_131459_move_wildcard_to_server', 33);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (34, '2023_06_23_084605_remove_wildcard_domain_from_instancesettings', 34);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (35, '2023_06_23_110548_next_channel_updates', 35);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (36, '2023_06_23_114131_change_env_var_value_length', 36);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (37, '2023_06_23_114132_remove_default_redirect_from_instance_settings', 37);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (38, '2023_06_23_114133_use_application_deployment_queues_as_activity', 38);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (39, '2023_06_23_114134_add_disk_usage_percentage_to_servers', 39);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (40, '2023_07_13_115117_create_subscriptions_table', 40);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (41, '2023_07_13_120719_create_webhooks_table', 41);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (42, '2023_07_13_120721_add_license_to_instance_settings', 42);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (43, '2023_07_27_182013_smtp_discord_schemaless_to_normal', 43);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (44, '2023_08_06_142951_add_description_field_to_applications_table', 44);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (45, '2023_08_06_142952_remove_foreignId_environment_variables', 45);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (46, '2023_08_06_142954_add_readonly_localpersistentvolumes', 46);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (47, '2023_08_07_073651_create_s3_storages_table', 47);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (48, '2023_08_07_142950_create_standalone_postgresqls_table', 48);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (49, '2023_08_08_150103_create_scheduled_database_backups_table', 49);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (50, '2023_08_10_113306_create_scheduled_database_backup_executions_table', 50);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (51, '2023_08_10_201311_add_backup_notifications_to_teams', 51);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (52, '2023_08_11_190528_add_dockerfile_to_applications_table', 52);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (53, '2023_08_15_095902_create_waitlists_table', 53);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (54, '2023_08_15_111125_update_users_table', 54);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (55, '2023_08_15_111126_update_servers_add_unreachable_count_table', 55);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (56, '2023_08_22_071048_add_boarding_to_teams', 56);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (57, '2023_08_22_071049_update_webhooks_type', 57);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (58, '2023_08_22_071050_update_subscriptions_stripe', 58);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (59, '2023_08_22_071051_add_stripe_plan_to_subscriptions', 59);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (60, '2023_08_22_071052_add_resend_as_email', 60);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (61, '2023_08_22_071053_add_resend_as_email_to_teams', 61);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (62, '2023_08_22_071054_add_stripe_reasons', 62);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (63, '2023_08_22_071055_add_discord_notifications_to_teams', 63);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (64, '2023_08_22_071056_update_telegram_notifications', 64);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (65, '2023_08_22_071057_add_nixpkgsarchive_to_applications', 65);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (66, '2023_08_22_071058_add_nixpkgsarchive_to_applications_remove', 66);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (67, '2023_08_22_071059_add_stripe_trial_ended', 67);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (68, '2023_08_22_071060_change_invitation_link_length', 68);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (69, '2023_09_20_082541_update_services_table', 69);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (70, '2023_09_20_082733_create_service_databases_table', 70);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (71, '2023_09_20_082737_create_service_applications_table', 71);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (72, '2023_09_20_083549_update_environment_variables_table', 72);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (73, '2023_09_22_185356_create_local_file_volumes_table', 73);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (74, '2023_09_23_111808_update_servers_with_cloudflared', 74);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (75, '2023_09_23_111809_remove_destination_from_services_table', 75);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (76, '2023_09_23_111811_update_service_applications_table', 76);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (77, '2023_09_23_111812_update_service_databases_table', 77);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (78, '2023_09_23_111813_update_users_databases_table', 78);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (79, '2023_09_23_111814_update_local_file_volumes_table', 79);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (80, '2023_09_23_111815_add_healthcheck_disable_to_apps_table', 80);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (81, '2023_09_23_111816_add_destination_to_services_table', 81);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (82, '2023_09_23_111817_use_instance_email_settings_by_default', 82);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (83, '2023_09_23_111818_set_notifications_on_by_default', 83);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (84, '2023_09_23_111819_add_server_emails', 84);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (85, '2023_10_08_111819_add_server_unreachable_count', 85);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (86, '2023_10_10_100320_update_s3_storages_table', 86);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (87, '2023_10_10_113144_add_dockerfile_location_applications_table', 87);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (88, '2023_10_12_132430_create_standalone_redis_table', 88);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (89, '2023_10_12_132431_add_standalone_redis_to_environment_variables_table', 89);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (90, '2023_10_12_132432_add_database_selection_to_backups', 90);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (91, '2023_10_18_072519_add_custom_labels_applications_table', 91);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (92, '2023_10_19_101331_create_standalone_mongodbs_table', 92);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (93, '2023_10_19_101332_add_standalone_mongodb_to_environment_variables_table', 93);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (94, '2023_10_24_103548_create_standalone_mysqls_table', 94);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (95, '2023_10_24_120523_create_standalone_mariadbs_table', 95);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (96, '2023_10_24_120524_add_standalone_mysql_to_environment_variables_table', 96);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (97, '2023_10_24_124934_add_is_shown_once_to_environment_variables_table', 97);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (98, '2023_11_01_100437_add_restart_to_deployment_queue', 98);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (99, '2023_11_07_123731_add_target_build_dockerfile', 99);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (100, '2023_11_08_112815_add_custom_config_standalone_postgresql', 100);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (101, '2023_11_09_133332_add_public_port_to_service_databases', 101);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (102, '2023_11_12_180605_change_fqdn_to_longer_field', 102);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (103, '2023_11_13_133059_add_sponsorship_disable', 103);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (104, '2023_11_14_103450_add_manual_webhook_secret', 104);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (105, '2023_11_14_121416_add_git_type', 105);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (106, '2023_11_16_101819_add_high_disk_usage_notification', 106);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (107, '2023_11_16_220647_add_log_drains', 107);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (108, '2023_11_17_160437_add_drain_log_enable_by_service', 108);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (109, '2023_11_20_094628_add_gpu_settings', 109);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (110, '2023_11_21_121920_add_additional_destinations_to_apps', 110);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (111, '2023_11_24_080341_add_docker_compose_location', 111);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (112, '2023_11_28_143533_add_fields_to_swarm_dockers', 112);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (113, '2023_11_29_075937_change_swarm_properties', 113);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (114, '2023_12_01_091723_save_logs_view_settings', 114);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (115, '2023_12_01_095356_add_custom_fluentd_config_for_logdrains', 115);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (116, '2023_12_08_162228_add_soft_delete_services', 116);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (117, '2023_12_11_103611_add_realtime_connection_problem', 117);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (118, '2023_12_13_110214_add_soft_deletes', 118);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (119, '2023_12_17_155616_add_custom_docker_compose_start_command', 119);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (120, '2023_12_18_093514_add_swarm_related_things', 120);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (121, '2023_12_19_124111_add_swarm_cluster_grouping', 121);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (122, '2023_12_30_134507_add_description_to_environments', 122);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (123, '2023_12_31_173041_create_scheduled_tasks_table', 123);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (124, '2024_01_01_231053_create_scheduled_task_executions_table', 124);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (125, '2024_01_02_113855_add_raw_compose_deployment', 125);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (126, '2024_01_12_123422_update_cpuset_limits', 126);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (127, '2024_01_15_084609_add_custom_dns_server', 127);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (128, '2024_01_16_115005_add_build_server_enable', 128);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (129, '2024_01_21_130328_add_docker_network_to_services', 129);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (130, '2024_01_23_095832_add_manual_webhook_secret_bitbucket', 130);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (131, '2024_01_23_113129_create_shared_environment_variables_table', 131);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (132, '2024_01_24_095449_add_concurrent_number_of_builds_per_server', 132);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (133, '2024_01_25_073212_add_server_id_to_queues', 133);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (134, '2024_01_27_164724_add_application_name_and_deployment_url_to_queue', 134);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (135, '2024_01_29_072322_change_env_variable_length', 135);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (136, '2024_01_29_145200_add_custom_docker_run_options', 136);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (137, '2024_02_01_111228_create_tags_table', 137);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (138, '2024_02_05_105215_add_destination_to_app_deployments', 138);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (139, '2024_02_06_132748_add_additional_destinations', 139);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (140, '2024_02_08_075523_add_post_deployment_to_applications', 140);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (141, '2024_02_08_112304_add_dynamic_timeout_for_deployments', 141);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (142, '2024_02_15_101921_add_consistent_application_container_name', 142);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (143, '2024_02_15_192025_add_is_gzip_enabled_to_services', 143);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (144, '2024_02_20_165045_add_permissions_to_github_app', 144);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (145, '2024_02_22_090900_add_only_this_server_deployment', 145);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (146, '2024_02_23_143119_add_custom_server_limits_to_teams_ultimate', 146);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (147, '2024_02_25_222150_add_server_force_disabled_field', 147);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (148, '2024_03_04_092244_add_gzip_enabled_and_stripprefix_settings', 148);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (149, '2024_03_07_115054_add_notifications_notification_disable', 149);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (150, '2024_03_08_180457_nullable_password', 150);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (151, '2024_03_11_150013_create_oauth_settings', 151);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (152, '2024_03_14_214402_add_multiline_envs', 152);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (153, '2024_03_18_101440_add_version_of_envs', 153);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (154, '2024_03_22_080914_remove_popup_notifications', 154);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (155, '2024_03_26_122110_remove_realtime_notifications', 155);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (156, '2024_03_28_114620_add_watch_paths_to_apps', 156);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (157, '2024_04_09_095517_make_custom_docker_commands_longer', 157);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (158, '2024_04_10_071920_create_standalone_keydbs_table', 158);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (159, '2024_04_10_082220_create_standalone_dragonflies_table', 159);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (160, '2024_04_10_091519_create_standalone_clickhouses_table', 160);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (161, '2024_04_10_124015_add_permission_local_file_volumes', 161);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (162, '2024_04_12_092337_add_config_hash_to_other_resources', 162);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (163, '2024_04_15_094703_add_literal_variables', 163);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (164, '2024_04_16_083919_add_service_type_on_creation', 164);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (165, '2024_04_17_132541_add_rollback_queues', 165);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (166, '2024_04_25_073615_add_docker_network_to_application_settings', 166);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (167, '2024_04_29_111956_add_custom_hc_indicator_apps', 167);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (168, '2024_05_06_093236_add_custom_name_to_application_settings', 168);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (169, '2024_05_07_124019_add_server_metrics', 169);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (170, '2024_05_10_085215_make_stripe_comment_longer', 170);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (171, '2024_05_15_091757_add_commit_message_to_app_deployment_queue', 171);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (172, '2024_05_15_151236_add_container_escape_toggle', 172);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (173, '2024_05_17_082012_add_env_sorting_toggle', 173);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (174, '2024_05_21_125739_add_scheduled_tasks_notification_to_teams', 174);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (175, '2024_05_22_103942_change_pre_post_deployment_commands_length_in_applications', 175);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (176, '2024_05_23_091713_add_gitea_webhook_to_applications', 176);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (177, '2024_06_05_101019_add_docker_compose_pr_domains', 177);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (178, '2024_06_06_103938_change_pr_issue_commend_id_type', 178);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (179, '2024_06_11_081614_add_www_non_www_redirect', 179);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (180, '2024_06_18_105948_move_server_metrics', 180);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (181, '2024_06_20_102551_add_server_api_sentinel', 181);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (182, '2024_06_21_143358_add_api_deployment_type', 182);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (183, '2024_06_22_081140_alter_instance_settings_add_instance_name', 183);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (184, '2024_06_25_184323_update_db', 184);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (185, '2024_07_01_115528_add_is_api_allowed_and_iplist', 185);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (186, '2024_07_05_120217_remove_unique_from_tag_names', 186);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (187, '2024_07_11_083719_application_compose_versions', 187);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (188, '2024_07_17_123828_add_is_container_labels_readonly', 188);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (189, '2024_07_18_110424_create_application_settings_is_preserve_repository_enabled', 189);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (190, '2024_07_18_123458_add_force_cleanup_server', 190);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (191, '2024_07_19_132617_disable_healtcheck_by_default', 191);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (192, '2024_07_23_112710_add_validation_logs_to_servers', 192);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (193, '2024_08_05_142659_add_update_frequency_settings', 193);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (194, '2024_08_07_155324_add_proxy_label_chooser', 194);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (195, '2024_08_09_215659_add_server_cleanup_fields_to_server_settings_table', 195);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (196, '2024_08_12_131659_add_local_file_volume_based_on_git', 196);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (197, '2024_08_12_155023_add_timezone_to_server_and_instance_settings', 197);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (198, '2024_08_14_183120_add_order_to_environment_variables_table', 198);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (199, '2024_08_15_115907_add_build_server_id_to_deployment_queue', 199);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (200, '2024_08_16_105649_add_custom_docker_options_to_dbs', 200);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (201, '2024_08_27_090528_add_compose_parsing_version_to_services', 201);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (202, '2024_09_05_085700_add_helper_version_to_instance_settings', 202);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (203, '2024_09_06_062534_change_server_cleanup_to_forced', 203);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (204, '2024_09_07_185402_change_cleanup_schedule', 204);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (205, '2024_09_08_130756_update_server_settings_default_timezone', 205);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (206, '2024_09_16_111428_encrypt_existing_private_keys', 206);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (207, '2024_09_17_111226_add_ssh_key_fingerprint_to_private_keys_table', 207);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (208, '2024_09_22_165240_add_advanced_options_to_cleanup_options_to_servers_settings_table', 208);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (209, '2024_09_26_083441_disable_api_by_default', 209);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (210, '2024_10_03_095427_add_dump_all_to_standalone_postgresqls', 210);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (211, '2024_10_10_081444_remove_constraint_from_service_applications_fqdn', 211);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (212, '2024_10_11_114331_add_required_env_variables', 212);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (213, '2024_10_14_090416_update_metrics_token_in_server_settings', 213);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (214, '2024_10_15_172139_add_is_shared_to_environment_variables', 214);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (215, '2024_10_16_120026_move_redis_password_to_envs', 215);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (216, '2024_10_16_192133_add_confirmation_settings_to_instance_settings_table', 216);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (217, '2024_10_17_093722_add_soft_delete_to_servers', 217);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (218, '2024_10_22_105745_add_server_disk_usage_threshold', 218);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (219, '2024_10_22_121223_add_server_disk_usage_notification', 219);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (220, '2024_10_29_093927_add_is_sentinel_debug_enabled_to_server_settings', 220);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (221, '2024_10_30_074601_rename_token_permissions', 221);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (222, '2024_11_02_213214_add_last_online_at_to_resources', 222);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (223, '2024_11_11_125335_add_custom_nginx_configuration_to_static', 223);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (224, '2024_11_11_125366_add_index_to_activity_log', 224);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (225, '2024_11_22_124742_add_uuid_to_environments_table', 225);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (226, '2024_12_05_091823_add_disable_build_cache_advanced_option', 226);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (227, '2024_12_05_212355_create_email_notification_settings_table', 227);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (228, '2024_12_05_212416_create_discord_notification_settings_table', 228);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (229, '2024_12_05_212440_create_telegram_notification_settings_table', 229);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (230, '2024_12_05_212546_migrate_email_notification_settings_from_teams_table', 230);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (231, '2024_12_05_212631_migrate_discord_notification_settings_from_teams_table', 231);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (232, '2024_12_05_212705_migrate_telegram_notification_settings_from_teams_table', 232);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (233, '2024_12_06_142014_create_slack_notification_settings_table', 233);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (234, '2024_12_09_105711_drop_waitlists_table', 234);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (235, '2024_12_10_122142_encrypt_instance_settings_email_columns', 235);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (236, '2024_12_10_122143_drop_resale_license', 236);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (237, '2024_12_11_135026_create_pushover_notification_settings_table', 237);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (238, '2024_12_11_161418_add_authentik_base_url_to_oauth_settings_table', 238);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (239, '2024_12_13_103007_encrypt_resend_api_key_in_instance_settings', 239);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (240, '2024_12_16_134437_add_resourceable_columns_to_environment_variables_table', 240);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (241, '2024_12_17_140637_add_server_disk_usage_check_frequency_to_server_settings_table', 241);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (242, '2024_12_23_142402_update_email_encryption_values', 242);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (243, '2025_01_05_050736_add_network_aliases_to_applications_table', 243);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (244, '2025_01_08_154008_switch_up_readonly_labels', 244);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (245, '2025_01_10_135244_add_horizon_job_details_to_queue', 245);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (246, '2025_01_13_130238_add_backup_retention_fields_to_scheduled_database_backups_table', 246);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (247, '2025_01_15_130416_create_docker_cleanup_executions_table', 247);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (248, '2025_01_16_110406_change_commit_message_to_text_in_application_deployment_queues', 248);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (249, '2025_01_16_130238_add_finished_at_to_executions_tables', 249);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (250, '2025_01_21_125205_update_finished_at_timestamps_if_not_set', 250);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (251, '2025_01_22_101105_remove_wrongly_created_envs', 251);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (252, '2025_01_27_102616_add_ssl_fields_to_database_tables', 252);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (253, '2025_01_27_153741_create_ssl_certificates_table', 253);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (254, '2025_01_30_125223_encrypt_local_file_volumes_fields', 254);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (255, '2025_02_27_125249_add_index_to_scheduled_task_executions', 255);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (256, '2025_03_01_112617_add_stripe_past_due', 256);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (257, '2025_03_14_140150_add_storage_deletion_tracking_to_backup_executions', 257);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (258, '2025_03_21_104103_disable_discord_here', 258);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (259, '2025_03_26_104103_disable_mongodb_ssl_by_default', 259);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (260, '2025_03_29_204400_revert_some_local_volume_encryption', 260);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (261, '2025_03_31_124212_add_specific_spa_configuration', 261);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (262, '2025_04_01_124212_stripe_comment_nullable', 262);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (263, '2025_04_17_110026_add_application_http_basic_auth_fields', 263);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (264, '2025_04_30_134146_add_is_migrated_to_services', 264);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (265, '2025_05_26_100258_add_server_patch_notifications', 265);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (266, '2025_05_29_100258_add_terminal_enabled_to_server_settings', 266);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (267, '2025_06_06_073345_create_server_previous_ip', 267);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (268, '2025_06_16_123532_change_sentinel_on_by_default', 268);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (269, '2025_06_25_131350_add_is_sponsorship_popup_enabled_to_instance_settings_table', 269);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (270, '2025_06_26_131350_optimize_activity_log_indexes', 270);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (271, '2025_07_14_191016_add_deleted_at_to_application_previews_table', 271);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (272, '2025_07_16_202201_add_timeout_to_scheduled_database_backups_table', 272);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (273, '2025_08_07_142403_create_user_changelog_reads_table', 273);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (274, '2025_08_17_102422_add_disable_local_backup_to_scheduled_database_backups_table', 274);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (275, '2025_08_18_104146_add_email_change_fields_to_users_table', 275);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (276, '2025_08_18_154244_change_env_sorting_default_to_false', 276);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (277, '2025_08_21_080234_add_git_shallow_clone_to_application_settings_table', 277);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (278, '2025_09_05_142446_add_pr_deployments_public_enabled_to_application_settings', 278);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (279, '2025_09_10_172952_remove_is_readonly_from_local_persistent_volumes_table', 279);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (280, '2025_09_10_173300_drop_webhooks_table', 280);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (281, '2025_09_10_173402_drop_kubernetes_table', 281);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (282, '2025_09_11_143432_remove_is_build_time_from_environment_variables_table', 282);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (283, '2025_09_11_150344_add_is_buildtime_only_to_environment_variables_table', 283);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (284, '2025_09_17_081112_add_use_build_secrets_to_application_settings', 284);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (285, '2025_09_18_080152_add_runtime_and_buildtime_to_environment_variables_table', 285);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (286, '2025_10_03_154100_update_clickhouse_image', 286);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (287, '2025_10_07_120723_add_s3_uploaded_to_scheduled_database_backup_executions_table', 287);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (288, '2025_10_08_181125_create_cloud_provider_tokens_table', 288);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (289, '2025_10_08_185203_add_hetzner_server_id_to_servers_table', 289);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (290, '2025_10_09_095905_add_cloud_provider_token_id_to_servers_table', 290);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (291, '2025_10_09_113602_add_hetzner_server_status_to_servers_table', 291);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (292, '2025_10_09_125036_add_is_validating_to_servers_table', 292);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (293, '2025_11_02_161923_add_dev_helper_version_to_instance_settings', 293);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (294, '2025_11_09_000001_add_timeout_to_scheduled_tasks_table', 294);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (295, '2025_11_09_000002_improve_scheduled_task_executions_tracking', 295);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (296, '2025_11_10_112500_add_restart_tracking_to_applications_table', 296);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (297, '2025_11_12_130931_add_traefik_version_tracking_to_servers_table', 297);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (298, '2025_11_12_131252_add_traefik_outdated_to_email_notification_settings', 298);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (299, '2025_11_12_133400_add_traefik_outdated_thread_id_to_telegram_notification_settings', 299);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (300, '2025_11_14_114632_add_traefik_outdated_info_to_servers_table', 300);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (301, '2025_11_16_000001_create_webhook_notification_settings_table', 301);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (302, '2025_11_16_000002_create_cloud_init_scripts_table', 302);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (303, '2025_11_17_092707_add_traefik_outdated_to_notification_settings', 303);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (304, '2025_11_18_083747_cleanup_dockerfile_data_for_non_dockerfile_buildpacks', 304);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (305, '2025_11_26_124200_add_build_cache_settings_to_application_settings', 305);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (306, '2025_11_28_000001_migrate_clickhouse_to_official_image', 306);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (307, '2025_12_04_134435_add_deployment_queue_limit_to_server_settings', 307);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (308, '2025_12_05_000000_add_docker_images_to_keep_to_application_settings', 308);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (309, '2025_12_05_100000_add_disable_application_image_retention_to_server_settings', 309);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (310, '2025_12_08_135600_add_performance_indexes', 310);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (311, '2025_12_10_135600_add_uuid_to_cloud_provider_tokens', 311);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (312, '2025_12_15_143052_trim_s3_storage_credentials', 312);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (313, '2025_12_17_000001_add_is_wire_navigate_enabled_to_instance_settings_table', 313);
+INSERT INTO "migrations" ("id", "migration", "batch") VALUES (314, '2025_12_17_000002_add_restart_tracking_to_standalone_databases', 314);
diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml
index 9e4c6b8b1..acc84b61a 100644
--- a/docker-compose.dev.yml
+++ b/docker-compose.dev.yml
@@ -26,6 +26,8 @@ services:
volumes:
- .:/var/www/html/:cached
- dev_backups_data:/var/www/html/storage/app/backups
+ networks:
+ - coolify
postgres:
pull_policy: always
ports:
diff --git a/package-lock.json b/package-lock.json
index 6244197fb..59d678c4f 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -10,7 +10,8 @@
"@tailwindcss/typography": "0.5.16",
"@xterm/addon-fit": "0.10.0",
"@xterm/xterm": "5.5.0",
- "ioredis": "5.6.1"
+ "ioredis": "5.6.1",
+ "playwright": "^1.58.2"
},
"devDependencies": {
"@tailwindcss/postcss": "4.1.18",
@@ -2338,6 +2339,50 @@
"url": "https://github.com/sponsors/jonschlinkert"
}
},
+ "node_modules/playwright": {
+ "version": "1.58.2",
+ "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.58.2.tgz",
+ "integrity": "sha512-vA30H8Nvkq/cPBnNw4Q8TWz1EJyqgpuinBcHET0YVJVFldr8JDNiU9LaWAE1KqSkRYazuaBhTpB5ZzShOezQ6A==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "playwright-core": "1.58.2"
+ },
+ "bin": {
+ "playwright": "cli.js"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "optionalDependencies": {
+ "fsevents": "2.3.2"
+ }
+ },
+ "node_modules/playwright-core": {
+ "version": "1.58.2",
+ "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.58.2.tgz",
+ "integrity": "sha512-yZkEtftgwS8CsfYo7nm0KE8jsvm6i/PTgVtB8DL726wNf6H2IMsDuxCpJj59KDaxCtSnrWan2AeDqM7JBaultg==",
+ "license": "Apache-2.0",
+ "bin": {
+ "playwright-core": "cli.js"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/playwright/node_modules/fsevents": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
+ "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
+ "hasInstallScript": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
"node_modules/postcss": {
"version": "8.5.6",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
diff --git a/package.json b/package.json
index dc4b912ea..81cd8c9a4 100644
--- a/package.json
+++ b/package.json
@@ -24,6 +24,7 @@
"@tailwindcss/typography": "0.5.16",
"@xterm/addon-fit": "0.10.0",
"@xterm/xterm": "5.5.0",
- "ioredis": "5.6.1"
+ "ioredis": "5.6.1",
+ "playwright": "^1.58.2"
}
-}
\ No newline at end of file
+}
diff --git a/phpunit.xml b/phpunit.xml
index 38adfdb6f..6716b6b84 100644
--- a/phpunit.xml
+++ b/phpunit.xml
@@ -7,17 +7,20 @@
./tests/Feature
+
+ ./tests/v4
+
-
-
-
-
-
-
+
+
+
+
+
+
diff --git a/resources/views/auth/register.blade.php b/resources/views/auth/register.blade.php
index 0cf1a2772..f7ab57a14 100644
--- a/resources/views/auth/register.blade.php
+++ b/resources/views/auth/register.blade.php
@@ -1,7 +1,9 @@
environment('local') ? $localValue : '');
+if (! function_exists('getOldOrLocal')) {
+ function getOldOrLocal($key, $localValue)
+ {
+ return old($key) != '' ? old($key) : (app()->environment('local') ? $localValue : '');
+ }
}
$name = getOldOrLocal('name', 'test3 normal user');
diff --git a/tests/Browser/screenshots/.gitignore b/tests/Browser/screenshots/.gitignore
deleted file mode 100644
index d6b7ef32c..000000000
--- a/tests/Browser/screenshots/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-*
-!.gitignore
diff --git a/tests/Pest.php b/tests/Pest.php
index 619dea153..cec77b86f 100644
--- a/tests/Pest.php
+++ b/tests/Pest.php
@@ -13,7 +13,7 @@
| need to change it using the "uses()" function to bind a different classes or traits.
|
*/
-uses(Tests\TestCase::class)->in('Feature');
+uses(Tests\TestCase::class)->in('Feature', 'v4/Feature', 'v4/Browser');
/*
|--------------------------------------------------------------------------
diff --git a/tests/v4/Browser/DashboardTest.php b/tests/v4/Browser/DashboardTest.php
new file mode 100644
index 000000000..b4a97f268
--- /dev/null
+++ b/tests/v4/Browser/DashboardTest.php
@@ -0,0 +1,162 @@
+ 0]);
+
+ $this->user = User::factory()->create([
+ 'id' => 0,
+ 'name' => 'Root User',
+ 'email' => 'test@example.com',
+ 'password' => Hash::make('password'),
+ ]);
+
+ PrivateKey::create([
+ 'id' => 1,
+ 'uuid' => 'ssh-test',
+ 'team_id' => 0,
+ 'name' => 'Test Key',
+ 'description' => 'Test SSH key',
+ 'private_key' => '-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
+QyNTUxOQAAACBbhpqHhqv6aI67Mj9abM3DVbmcfYhZAhC7ca4d9UCevAAAAJi/QySHv0Mk
+hwAAAAtzc2gtZWQyNTUxOQAAACBbhpqHhqv6aI67Mj9abM3DVbmcfYhZAhC7ca4d9UCevA
+AAAECBQw4jg1WRT2IGHMncCiZhURCts2s24HoDS0thHnnRKVuGmoeGq/pojrsyP1pszcNV
+uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
+-----END OPENSSH PRIVATE KEY-----',
+ ]);
+
+ Server::create([
+ 'id' => 0,
+ 'uuid' => 'localhost',
+ 'name' => 'localhost',
+ 'description' => 'This is a test docker container in development mode',
+ 'ip' => 'coolify-testing-host',
+ 'team_id' => 0,
+ 'private_key_id' => 1,
+ 'proxy' => [
+ 'type' => ProxyTypes::TRAEFIK->value,
+ 'status' => ProxyStatus::EXITED->value,
+ ],
+ ]);
+
+ Server::create([
+ 'uuid' => 'production-1',
+ 'name' => 'production-web',
+ 'description' => 'Production web server cluster',
+ 'ip' => '10.0.0.1',
+ 'team_id' => 0,
+ 'private_key_id' => 1,
+ 'proxy' => [
+ 'type' => ProxyTypes::TRAEFIK->value,
+ 'status' => ProxyStatus::EXITED->value,
+ ],
+ ]);
+
+ Server::create([
+ 'uuid' => 'staging-1',
+ 'name' => 'staging-server',
+ 'description' => 'Staging environment server',
+ 'ip' => '10.0.0.2',
+ 'team_id' => 0,
+ 'private_key_id' => 1,
+ 'proxy' => [
+ 'type' => ProxyTypes::TRAEFIK->value,
+ 'status' => ProxyStatus::EXITED->value,
+ ],
+ ]);
+
+ Project::create([
+ 'uuid' => 'project-1',
+ 'name' => 'My first project',
+ 'description' => 'This is a test project in development',
+ 'team_id' => 0,
+ ]);
+
+ Project::create([
+ 'uuid' => 'project-2',
+ 'name' => 'Production API',
+ 'description' => 'Backend services for production',
+ 'team_id' => 0,
+ ]);
+
+ Project::create([
+ 'uuid' => 'project-3',
+ 'name' => 'Staging Environment',
+ 'description' => 'Staging and QA testing',
+ 'team_id' => 0,
+ ]);
+});
+
+function loginAndSkipOnboarding(): mixed
+{
+ return visit('/login')
+ ->fill('email', 'test@example.com')
+ ->fill('password', 'password')
+ ->click('Login')
+ ->click('Skip Setup');
+}
+
+it('redirects to login when not authenticated', function () {
+ $page = visit('/');
+
+ $page->assertPathIs('/login')
+ ->screenshot();
+});
+
+it('shows onboarding after first login', function () {
+ $page = visit('/login');
+
+ $page->fill('email', 'test@example.com')
+ ->fill('password', 'password')
+ ->click('Login')
+ ->assertSee('Welcome to Coolify')
+ ->assertSee("Let's go!")
+ ->assertSee('Skip Setup')
+ ->screenshot();
+});
+
+it('shows dashboard after skipping onboarding', function () {
+ $page = loginAndSkipOnboarding();
+
+ $page->assertSee('Dashboard')
+ ->assertSee('Your self-hosted infrastructure.')
+ ->screenshot();
+});
+
+it('shows all projects on dashboard', function () {
+ $page = loginAndSkipOnboarding();
+
+ $page->assertSee('Projects')
+ ->assertSee('My first project')
+ ->assertSee('This is a test project in development')
+ ->assertSee('Production API')
+ ->assertSee('Backend services for production')
+ ->assertSee('Staging Environment')
+ ->assertSee('Staging and QA testing')
+ ->screenshot();
+});
+
+it('shows servers on dashboard', function () {
+ $page = loginAndSkipOnboarding();
+
+ $page->assertSee('Servers')
+ ->assertSee('localhost')
+ ->assertSee('This is a test docker container in development mode')
+ ->assertSee('production-web')
+ ->assertSee('Production web server cluster')
+ ->assertSee('staging-server')
+ ->assertSee('Staging environment server')
+ ->screenshot();
+});
diff --git a/tests/v4/Browser/LoginTest.php b/tests/v4/Browser/LoginTest.php
new file mode 100644
index 000000000..7666e07e2
--- /dev/null
+++ b/tests/v4/Browser/LoginTest.php
@@ -0,0 +1,52 @@
+ 0]);
+});
+
+it('shows registration page when no users exist', function () {
+ $page = visit('/login');
+
+ $page->assertSee('Root User Setup')
+ ->assertSee('Create Account')
+ ->screenshot();
+});
+
+it('can login with valid credentials', function () {
+ User::factory()->create([
+ 'id' => 0,
+ 'email' => 'test@example.com',
+ 'password' => Hash::make('password'),
+ ]);
+
+ $page = visit('/login');
+
+ $page->fill('email', 'test@example.com')
+ ->fill('password', 'password')
+ ->click('Login')
+ ->assertSee('Welcome to Coolify')
+ ->screenshot();
+});
+
+it('fails login with invalid credentials', function () {
+ User::factory()->create([
+ 'id' => 0,
+ 'email' => 'test@example.com',
+ 'password' => Hash::make('password'),
+ ]);
+
+ $page = visit('/login');
+
+ $page->fill('email', 'random@email.com')
+ ->fill('password', 'wrongpassword123')
+ ->click('Login')
+ ->assertSee('These credentials do not match our records')
+ ->screenshot();
+});
diff --git a/tests/v4/Browser/RegistrationTest.php b/tests/v4/Browser/RegistrationTest.php
new file mode 100644
index 000000000..e2a232357
--- /dev/null
+++ b/tests/v4/Browser/RegistrationTest.php
@@ -0,0 +1,67 @@
+ 0]);
+});
+
+it('shows registration page when no users exist', function () {
+ $page = visit('/register');
+
+ $page->assertSee('Root User Setup')
+ ->assertSee('Create Account')
+ ->screenshot();
+});
+
+it('can register a new root user', function () {
+ $page = visit('/register');
+
+ $page->fill('name', 'Test User')
+ ->fill('email', 'root@example.com')
+ ->fill('password', 'Password1!@')
+ ->fill('password_confirmation', 'Password1!@')
+ ->click('Create Account')
+ ->assertPathIs('/onboarding')
+ ->screenshot();
+
+ expect(User::where('email', 'root@example.com')->exists())->toBeTrue();
+});
+
+it('fails registration with mismatched passwords', function () {
+ $page = visit('/register');
+
+ $page->fill('name', 'Test User')
+ ->fill('email', 'root@example.com')
+ ->fill('password', 'Password1!@')
+ ->fill('password_confirmation', 'DifferentPass1!@')
+ ->click('Create Account')
+ ->assertSee('password')
+ ->screenshot();
+});
+
+it('fails registration with weak password', function () {
+ $page = visit('/register');
+
+ $page->fill('name', 'Test User')
+ ->fill('email', 'root@example.com')
+ ->fill('password', 'short')
+ ->fill('password_confirmation', 'short')
+ ->click('Create Account')
+ ->assertSee('password')
+ ->screenshot();
+});
+
+it('shows login link when a user already exists', function () {
+ User::factory()->create(['id' => 0]);
+
+ $page = visit('/register');
+
+ $page->assertSee('Already registered?')
+ ->assertDontSee('Root User Setup')
+ ->screenshot();
+});
diff --git a/tests/v4/Feature/SqliteDatabaseTest.php b/tests/v4/Feature/SqliteDatabaseTest.php
new file mode 100644
index 000000000..d2df5c0a6
--- /dev/null
+++ b/tests/v4/Feature/SqliteDatabaseTest.php
@@ -0,0 +1,18 @@
+getDriverName())->toBe('sqlite');
+});
+
+it('runs migrations successfully', function () {
+ expect(Schema::hasTable('users'))->toBeTrue();
+ expect(Schema::hasTable('teams'))->toBeTrue();
+ expect(Schema::hasTable('servers'))->toBeTrue();
+ expect(Schema::hasTable('applications'))->toBeTrue();
+});