refactor(v5): remove Project model and consolidate v5 migrations
Drop v5_projects table, Project model, and related alter-table migrations; fold cluster_id and nullable private_key_id into the initial v5_servers migration; rename migration files to v5_ prefix; strip cooldServers/privateKeys/teams props from HomeController and Home page.
This commit is contained in:
parent
5c23b9aaa5
commit
9566b259b2
10 changed files with 35 additions and 498 deletions
|
|
@ -20,33 +20,11 @@ class HomeController extends Controller
|
|||
{
|
||||
public function __invoke(Request $request, FluxHealth $fluxHealth): Response
|
||||
{
|
||||
/** @var User $user */
|
||||
$user = $request->user();
|
||||
$currentTeam = $request->attributes->get('v5.currentTeam');
|
||||
|
||||
return Inertia::render('Home', [
|
||||
'flux' => $fluxHealth->check(),
|
||||
'clusters' => $this->clusters($currentTeam),
|
||||
'cooldServers' => $this->cooldServers($currentTeam),
|
||||
'privateKeys' => $this->privateKeys($currentTeam),
|
||||
'currentTeam' => $currentTeam instanceof Team ? [
|
||||
'id' => $currentTeam->id,
|
||||
'name' => $currentTeam->name,
|
||||
'description' => $currentTeam->description,
|
||||
'role' => $currentTeam->pivot?->role ?? $user->roleInTeam($currentTeam->id),
|
||||
'personal' => $currentTeam->personal_team,
|
||||
] : null,
|
||||
'teams' => $user->teams()
|
||||
->select('teams.id', 'teams.name', 'teams.description', 'teams.personal_team')
|
||||
->orderBy('teams.name')
|
||||
->get()
|
||||
->map(fn (Team $team) => [
|
||||
'id' => $team->id,
|
||||
'name' => $team->name,
|
||||
'description' => $team->description,
|
||||
'role' => $team->pivot->role,
|
||||
'personal' => $team->personal_team,
|
||||
]),
|
||||
]);
|
||||
}
|
||||
|
||||
|
|
@ -124,27 +102,6 @@ private function recordBootstrappedServer(User $user, Team $team, PrivateKey $pr
|
|||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array{uuid: string, name: string}>
|
||||
*/
|
||||
private function privateKeys(mixed $currentTeam): array
|
||||
{
|
||||
if (! $currentTeam instanceof Team) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return PrivateKey::query()
|
||||
->where('team_id', $currentTeam->id)
|
||||
->select('uuid', 'name')
|
||||
->orderBy('name')
|
||||
->get()
|
||||
->map(fn (PrivateKey $privateKey) => [
|
||||
'uuid' => $privateKey->uuid,
|
||||
'name' => $privateKey->name,
|
||||
])
|
||||
->all();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array{id: string, name: string, description: string|null, serversCount: int, servers: array<int, array{id: string, name: string, host: string, status: string, capabilities: array<int, string>}>}>
|
||||
*/
|
||||
|
|
@ -175,31 +132,4 @@ private function clusters(mixed $currentTeam): array
|
|||
])
|
||||
->all();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array{id: string, host: string, sshUser: string, sshPort: int, status: string, capabilities: array<int, string>, builderEnabled: bool, builderCapacity: int, lastBootstrappedAt: string|null}>
|
||||
*/
|
||||
private function cooldServers(mixed $currentTeam): array
|
||||
{
|
||||
if (! $currentTeam instanceof Team) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return V5Server::query()
|
||||
->where('team_id', $currentTeam->id)
|
||||
->orderBy('name')
|
||||
->get()
|
||||
->map(fn (V5Server $server) => [
|
||||
'id' => (string) $server->id,
|
||||
'host' => $server->host,
|
||||
'sshUser' => $server->ssh_user,
|
||||
'sshPort' => $server->ssh_port,
|
||||
'status' => $server->status,
|
||||
'capabilities' => $server->capabilities ?? [],
|
||||
'builderEnabled' => $server->builder_enabled,
|
||||
'builderCapacity' => $server->builder_capacity,
|
||||
'lastBootstrappedAt' => $server->last_bootstrapped_at?->toISOString(),
|
||||
])
|
||||
->all();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,29 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models\V5;
|
||||
|
||||
use App\Models\Team;
|
||||
use App\Models\User;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class Project extends V5Model
|
||||
{
|
||||
protected $table = 'v5_projects';
|
||||
|
||||
protected $fillable = [
|
||||
'team_id',
|
||||
'created_by_user_id',
|
||||
'name',
|
||||
'description',
|
||||
];
|
||||
|
||||
public function team(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Team::class);
|
||||
}
|
||||
|
||||
public function creator(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class, 'created_by_user_id');
|
||||
}
|
||||
}
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('v5_projects', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('team_id')->constrained('teams')->cascadeOnDelete();
|
||||
$table->foreignId('created_by_user_id')->constrained('users')->cascadeOnDelete();
|
||||
$table->string('name');
|
||||
$table->text('description')->nullable();
|
||||
$table->timestamps();
|
||||
|
||||
$table->index(['team_id', 'name']);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('v5_projects');
|
||||
}
|
||||
};
|
||||
|
|
@ -14,6 +14,7 @@ public function up(): void
|
|||
Schema::create('v5_servers', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('team_id')->constrained('teams')->cascadeOnDelete();
|
||||
$table->foreignId('cluster_id')->nullable()->constrained('v5_clusters')->nullOnDelete();
|
||||
$table->foreignId('created_by_user_id')->constrained('users')->cascadeOnDelete();
|
||||
$table->foreignId('private_key_id')->nullable()->constrained('private_keys')->nullOnDelete();
|
||||
$table->string('name');
|
||||
|
|
@ -28,6 +29,7 @@ public function up(): void
|
|||
$table->timestamps();
|
||||
|
||||
$table->unique(['team_id', 'host', 'ssh_port']);
|
||||
$table->index(['team_id', 'cluster_id']);
|
||||
$table->index(['team_id', 'status']);
|
||||
});
|
||||
}
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('v5_servers', function (Blueprint $table) {
|
||||
$table->foreignId('cluster_id')
|
||||
->nullable()
|
||||
->after('team_id')
|
||||
->constrained('v5_clusters')
|
||||
->nullOnDelete();
|
||||
|
||||
$table->index(['team_id', 'cluster_id']);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('v5_servers', function (Blueprint $table) {
|
||||
$table->dropIndex(['team_id', 'cluster_id']);
|
||||
$table->dropConstrainedForeignId('cluster_id');
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('v5_servers', function (Blueprint $table) {
|
||||
$table->foreignId('private_key_id')->nullable()->change();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('v5_servers', function (Blueprint $table) {
|
||||
$table->foreignId('private_key_id')->nullable(false)->change();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
@ -1350,17 +1350,6 @@ CREATE TABLE IF NOT EXISTS "v5_servers" (
|
|||
"updated_at" TEXT
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "v5_projects" (
|
||||
"id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
"team_id" INTEGER NOT NULL,
|
||||
"cluster_id" INTEGER,
|
||||
"created_by_user_id" INTEGER NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"description" TEXT,
|
||||
"created_at" TEXT,
|
||||
"updated_at" TEXT
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "webhook_notification_settings" (
|
||||
"id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
"team_id" INTEGER NOT NULL,
|
||||
|
|
@ -1792,8 +1781,5 @@ INSERT INTO "migrations" ("id", "migration", "batch") VALUES (311, '2025_12_10_1
|
|||
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);
|
||||
INSERT INTO "migrations" ("id", "migration", "batch") VALUES (315, '2026_06_04_050157_create_v5_projects_table', 315);
|
||||
INSERT INTO "migrations" ("id", "migration", "batch") VALUES (316, '2026_06_16_130650_create_v5_servers_table', 316);
|
||||
INSERT INTO "migrations" ("id", "migration", "batch") VALUES (317, '2026_06_16_130649_create_v5_clusters_table', 317);
|
||||
INSERT INTO "migrations" ("id", "migration", "batch") VALUES (318, '2026_06_16_131229_add_cluster_id_to_v5_servers_table', 318);
|
||||
INSERT INTO "migrations" ("id", "migration", "batch") VALUES (319, '2026_06_16_132000_make_v5_server_private_key_nullable', 319);
|
||||
INSERT INTO "migrations" ("id", "migration", "batch") VALUES (316, '2026_06_16_130650_v5_create_servers_table', 316);
|
||||
INSERT INTO "migrations" ("id", "migration", "batch") VALUES (317, '2026_06_16_130649_v5_create_clusters_table', 317);
|
||||
|
|
|
|||
|
|
@ -1,75 +1,6 @@
|
|||
import { Head } from '@inertiajs/react';
|
||||
import { useState } from 'react';
|
||||
|
||||
export default function Home({ currentTeam, teams, flux, clusters, cooldServers, privateKeys }) {
|
||||
const firstPrivateKey = privateKeys[0]?.uuid || '';
|
||||
const [bootstrapResult, setBootstrapResult] = useState(null);
|
||||
const [bootstrapping, setBootstrapping] = useState(false);
|
||||
const [bootstrapForm, setBootstrapForm] = useState({
|
||||
host: '',
|
||||
ssh_user: 'root',
|
||||
ssh_port: '22',
|
||||
private_key_uuid: firstPrivateKey,
|
||||
wg_listen_port: '',
|
||||
wg_endpoint: '',
|
||||
enable_builder: true,
|
||||
builder_capacity: '2',
|
||||
});
|
||||
|
||||
function csrfToken() {
|
||||
return document.querySelector('meta[name="csrf-token"]')?.getAttribute('content') || '';
|
||||
}
|
||||
|
||||
function updateBootstrapForm(field, value) {
|
||||
setBootstrapForm((current) => ({
|
||||
...current,
|
||||
[field]: value,
|
||||
}));
|
||||
}
|
||||
|
||||
async function bootstrapCoolifyMesh(event) {
|
||||
event.preventDefault();
|
||||
setBootstrapping(true);
|
||||
setBootstrapResult(null);
|
||||
|
||||
const payload = {
|
||||
host: bootstrapForm.host,
|
||||
ssh_user: bootstrapForm.ssh_user,
|
||||
ssh_port: Number(bootstrapForm.ssh_port),
|
||||
private_key_uuid: bootstrapForm.private_key_uuid,
|
||||
enable_builder: bootstrapForm.enable_builder,
|
||||
builder_capacity: bootstrapForm.builder_capacity === '' ? null : Number(bootstrapForm.builder_capacity),
|
||||
wg_listen_port: bootstrapForm.wg_listen_port === '' ? null : Number(bootstrapForm.wg_listen_port),
|
||||
wg_endpoint: bootstrapForm.wg_endpoint || null,
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await fetch('/v5/coolify/bootstrap', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
'X-CSRF-TOKEN': csrfToken(),
|
||||
},
|
||||
body: JSON.stringify(payload),
|
||||
});
|
||||
const result = await response.json();
|
||||
|
||||
setBootstrapResult(result);
|
||||
} catch (error) {
|
||||
setBootstrapResult({
|
||||
successful: false,
|
||||
label: 'Bootstrap failed',
|
||||
message: 'Could not start the coolify bootstrap command.',
|
||||
output: null,
|
||||
errorOutput: null,
|
||||
exitCode: null,
|
||||
});
|
||||
} finally {
|
||||
setBootstrapping(false);
|
||||
}
|
||||
}
|
||||
|
||||
export default function Home({ flux, clusters }) {
|
||||
return (
|
||||
<>
|
||||
<Head title="V5" />
|
||||
|
|
@ -110,177 +41,6 @@ export default function Home({ currentTeam, teams, flux, clusters, cooldServers,
|
|||
</ul>
|
||||
)}
|
||||
</section>
|
||||
|
||||
<section aria-labelledby="coold-server-heading">
|
||||
<h2 id="coold-server-heading">coold servers</h2>
|
||||
|
||||
{cooldServers.length === 0 ? (
|
||||
<p>No coold serverss have been added yet.</p>
|
||||
) : (
|
||||
<ul>
|
||||
{cooldServers.map((host) => (
|
||||
<li key={host.id}>
|
||||
<strong>{host.host}</strong> ({host.status}) — SSH {host.sshUser}@{host.host}:{host.sshPort};{' '}
|
||||
{host.capabilities.join(', ')}; builder{' '}
|
||||
{host.builderEnabled
|
||||
? `enabled, capacity ${host.builderCapacity}`
|
||||
: 'disabled'}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
</section>
|
||||
|
||||
<section aria-labelledby="coolify-heading">
|
||||
<h2 id="coolify-heading">coolify</h2>
|
||||
|
||||
<form onSubmit={bootstrapCoolifyMesh}>
|
||||
<h3>Bootstrap server</h3>
|
||||
|
||||
<label>
|
||||
Host/IP
|
||||
<input
|
||||
type="text"
|
||||
value={bootstrapForm.host}
|
||||
onChange={(event) => updateBootstrapForm('host', event.target.value)}
|
||||
placeholder="203.0.113.10"
|
||||
required
|
||||
/>
|
||||
</label>
|
||||
|
||||
<label>
|
||||
SSH user
|
||||
<input
|
||||
type="text"
|
||||
value={bootstrapForm.ssh_user}
|
||||
onChange={(event) => updateBootstrapForm('ssh_user', event.target.value)}
|
||||
required
|
||||
/>
|
||||
</label>
|
||||
|
||||
<label>
|
||||
SSH port
|
||||
<input
|
||||
type="number"
|
||||
min="1"
|
||||
max="65535"
|
||||
value={bootstrapForm.ssh_port}
|
||||
onChange={(event) => updateBootstrapForm('ssh_port', event.target.value)}
|
||||
required
|
||||
/>
|
||||
</label>
|
||||
|
||||
<label>
|
||||
Private key
|
||||
<select
|
||||
value={bootstrapForm.private_key_uuid}
|
||||
onChange={(event) => updateBootstrapForm('private_key_uuid', event.target.value)}
|
||||
required
|
||||
>
|
||||
<option value="" disabled>Select a private key</option>
|
||||
{privateKeys.map((privateKey) => (
|
||||
<option key={privateKey.uuid} value={privateKey.uuid}>
|
||||
{privateKey.name}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</label>
|
||||
|
||||
<details>
|
||||
<summary>Advanced mesh options</summary>
|
||||
|
||||
<label>
|
||||
WireGuard listen port override
|
||||
<input
|
||||
type="number"
|
||||
min="1"
|
||||
max="65535"
|
||||
value={bootstrapForm.wg_listen_port}
|
||||
onChange={(event) => updateBootstrapForm('wg_listen_port', event.target.value)}
|
||||
/>
|
||||
</label>
|
||||
|
||||
<label>
|
||||
WireGuard endpoint override
|
||||
<input
|
||||
type="text"
|
||||
value={bootstrapForm.wg_endpoint}
|
||||
onChange={(event) => updateBootstrapForm('wg_endpoint', event.target.value)}
|
||||
placeholder="host.example:51821"
|
||||
/>
|
||||
</label>
|
||||
|
||||
<label>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={bootstrapForm.enable_builder}
|
||||
onChange={(event) => updateBootstrapForm('enable_builder', event.target.checked)}
|
||||
/>
|
||||
Enable builder
|
||||
</label>
|
||||
|
||||
<label>
|
||||
Builder capacity
|
||||
<input
|
||||
type="number"
|
||||
min="0"
|
||||
max="100"
|
||||
value={bootstrapForm.builder_capacity}
|
||||
onChange={(event) => updateBootstrapForm('builder_capacity', event.target.value)}
|
||||
/>
|
||||
</label>
|
||||
</details>
|
||||
|
||||
<button type="submit" disabled={bootstrapping || privateKeys.length === 0}>
|
||||
{bootstrapping ? 'Bootstrapping server...' : 'Bootstrap server'}
|
||||
</button>
|
||||
|
||||
{privateKeys.length === 0 ? (
|
||||
<p>Add a private key before bootstrapping a server.</p>
|
||||
) : null}
|
||||
</form>
|
||||
|
||||
{bootstrapResult ? (
|
||||
<div>
|
||||
<p>
|
||||
<strong>{bootstrapResult.label}</strong>
|
||||
</p>
|
||||
<p>{bootstrapResult.message}</p>
|
||||
{bootstrapResult.exitCode !== null ? (
|
||||
<p>Exit code: {bootstrapResult.exitCode}</p>
|
||||
) : null}
|
||||
{bootstrapResult.output ? <pre>{bootstrapResult.output}</pre> : null}
|
||||
{bootstrapResult.errorOutput ? <pre>{bootstrapResult.errorOutput}</pre> : null}
|
||||
</div>
|
||||
) : null}
|
||||
</section>
|
||||
|
||||
<h2>Current team</h2>
|
||||
|
||||
{currentTeam ? (
|
||||
<dl>
|
||||
<dt>Name</dt>
|
||||
<dd>{currentTeam.name}</dd>
|
||||
|
||||
<dt>Description</dt>
|
||||
<dd>{currentTeam.description || 'No description'}</dd>
|
||||
|
||||
<dt>Your role</dt>
|
||||
<dd>{currentTeam.role}</dd>
|
||||
</dl>
|
||||
) : (
|
||||
<p>No team selected.</p>
|
||||
)}
|
||||
|
||||
<h2>Your teams</h2>
|
||||
|
||||
<ul>
|
||||
{teams.map((team) => (
|
||||
<li key={team.id}>
|
||||
{team.name} ({team.role})
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</main>
|
||||
</>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -24,7 +24,6 @@
|
|||
|
||||
Schema::dropIfExists('v5_servers');
|
||||
Schema::dropIfExists('v5_clusters');
|
||||
Schema::dropIfExists('v5_projects');
|
||||
Schema::dropIfExists('private_keys');
|
||||
Schema::dropIfExists('team_user');
|
||||
Schema::dropIfExists('teams');
|
||||
|
|
@ -52,22 +51,9 @@
|
|||
->and($groups['v5.authenticated'])->toContain(EnsureCurrentTeam::class);
|
||||
});
|
||||
|
||||
it('creates v5 project tables in the shared database', function () {
|
||||
createSharedUserAndTeamTables();
|
||||
|
||||
$migration = include database_path('migrations/2026_06_04_050157_create_v5_projects_table.php');
|
||||
$migration->up();
|
||||
|
||||
expect(Schema::hasTable('v5_projects'))->toBeTrue()
|
||||
->and(Schema::hasColumns('v5_projects', [
|
||||
'id',
|
||||
'team_id',
|
||||
'created_by_user_id',
|
||||
'name',
|
||||
'description',
|
||||
'created_at',
|
||||
'updated_at',
|
||||
]))->toBeTrue();
|
||||
it('reuses existing projects instead of creating v5 projects', function () {
|
||||
expect(file_exists(database_path('migrations/2026_06_04_050157_v5_create_projects_table.php')))->toBeFalse()
|
||||
->and(file_exists(app_path('Models/V5/Project.php')))->toBeFalse();
|
||||
});
|
||||
|
||||
it('creates v5 cluster tables and lets each server belong to one cluster', function () {
|
||||
|
|
@ -75,7 +61,7 @@
|
|||
Schema::dropIfExists('v5_servers');
|
||||
Schema::dropIfExists('v5_clusters');
|
||||
|
||||
$clusterMigration = include database_path('migrations/2026_06_16_130649_create_v5_clusters_table.php');
|
||||
$clusterMigration = include database_path('migrations/2026_06_16_130649_v5_create_clusters_table.php');
|
||||
$clusterMigration->up();
|
||||
|
||||
expect(Schema::hasTable('v5_clusters'))->toBeTrue()
|
||||
|
|
@ -89,14 +75,9 @@
|
|||
'updated_at',
|
||||
]))->toBeTrue();
|
||||
|
||||
Schema::dropIfExists('v5_servers');
|
||||
|
||||
$serverMigration = include database_path('migrations/2026_06_16_130650_create_v5_servers_table.php');
|
||||
$serverMigration = include database_path('migrations/2026_06_16_130650_v5_create_servers_table.php');
|
||||
$serverMigration->up();
|
||||
|
||||
$serverClusterMigration = include database_path('migrations/2026_06_16_131229_add_cluster_id_to_v5_servers_table.php');
|
||||
$serverClusterMigration->up();
|
||||
|
||||
expect(Schema::hasColumn('v5_servers', 'cluster_id'))->toBeTrue();
|
||||
});
|
||||
|
||||
|
|
@ -106,15 +87,12 @@
|
|||
Schema::dropIfExists('v5_servers');
|
||||
Schema::dropIfExists('v5_clusters');
|
||||
|
||||
$clusterMigration = include database_path('migrations/2026_06_16_130649_create_v5_clusters_table.php');
|
||||
$clusterMigration = include database_path('migrations/2026_06_16_130649_v5_create_clusters_table.php');
|
||||
$clusterMigration->up();
|
||||
|
||||
$migration = include database_path('migrations/2026_06_16_130650_create_v5_servers_table.php');
|
||||
$migration = include database_path('migrations/2026_06_16_130650_v5_create_servers_table.php');
|
||||
$migration->up();
|
||||
|
||||
$serverClusterMigration = include database_path('migrations/2026_06_16_131229_add_cluster_id_to_v5_servers_table.php');
|
||||
$serverClusterMigration->up();
|
||||
|
||||
expect(Schema::hasTable('v5_servers'))->toBeTrue()
|
||||
->and(Schema::hasColumns('v5_servers', [
|
||||
'id',
|
||||
|
|
@ -136,20 +114,21 @@
|
|||
]))->toBeTrue();
|
||||
});
|
||||
|
||||
it('includes v5 project tables in the dev testing schema', function () {
|
||||
it('includes v5 tables in the dev testing schema', function () {
|
||||
$schema = file_get_contents(database_path('schema/testing-schema.sql'));
|
||||
|
||||
expect($schema)->toContain('CREATE TABLE IF NOT EXISTS "v5_projects"')
|
||||
->and($schema)->toContain('"team_id" INTEGER NOT NULL')
|
||||
expect($schema)->toContain('"team_id" INTEGER NOT NULL')
|
||||
->and($schema)->toContain('"created_by_user_id" INTEGER NOT NULL')
|
||||
->and($schema)->toContain('2026_06_04_050157_create_v5_projects_table')
|
||||
->and($schema)->not->toContain('CREATE TABLE IF NOT EXISTS "v5_projects"')
|
||||
->and($schema)->not->toContain('2026_06_04_050157_v5_create_projects_table')
|
||||
->and($schema)->toContain('CREATE TABLE IF NOT EXISTS "v5_servers"')
|
||||
->and($schema)->toContain('"cluster_id" INTEGER')
|
||||
->and($schema)->toContain('CREATE TABLE IF NOT EXISTS "v5_clusters"')
|
||||
->and($schema)->toContain('"private_key_id" INTEGER')
|
||||
->and($schema)->toContain('2026_06_16_130650_create_v5_servers_table')
|
||||
->and($schema)->toContain('2026_06_16_130649_create_v5_clusters_table')
|
||||
->and($schema)->toContain('2026_06_16_131229_add_cluster_id_to_v5_servers_table')
|
||||
->and($schema)->toContain('2026_06_16_130650_v5_create_servers_table')
|
||||
->and($schema)->toContain('2026_06_16_130649_v5_create_clusters_table')
|
||||
->and($schema)->not->toContain('2026_06_16_131229_add_cluster_id_to_v5_servers_table')
|
||||
->and($schema)->not->toContain('2026_06_16_132000_make_v5_server_private_key_nullable')
|
||||
->and($schema)->not->toContain('v5_hosts');
|
||||
});
|
||||
|
||||
|
|
@ -186,16 +165,20 @@
|
|||
->assertSee('Home', false)
|
||||
->assertDontSee('v5-ready', false)
|
||||
->assertDontSee('This page is served from Laravel through Inertia and React')
|
||||
->assertDontSee('Bootstrap server')
|
||||
->assertDontSee('privateKeys', false)
|
||||
->assertSee('Running')
|
||||
->assertSee('Flux is running.')
|
||||
->assertSee('"clusters":[]', false)
|
||||
->assertSee('"cooldServers":[]', false)
|
||||
->assertDontSee('cooldServers', false)
|
||||
->assertDontSee('coold-dev')
|
||||
->assertDontSee('100.64.0.1')
|
||||
->assertSee('V5 Shared Team')
|
||||
->assertSee('Shared team details')
|
||||
->assertSee('owner')
|
||||
->assertSee($user->email);
|
||||
->assertDontSee('Current team')
|
||||
->assertDontSee('Your teams')
|
||||
->assertDontSee('currentTeam', false)
|
||||
->assertDontSee('teams', false)
|
||||
->assertDontSee('V5 Shared Team')
|
||||
->assertDontSee('Shared team details');
|
||||
});
|
||||
|
||||
it('shows v5 clusters with their servers on the inertia shell', function () {
|
||||
|
|
@ -263,8 +246,8 @@
|
|||
->get('/v5')
|
||||
->assertSuccessful()
|
||||
->assertSessionHas('currentTeam')
|
||||
->assertSee('Auto Selected Team')
|
||||
->assertSee('admin');
|
||||
->assertDontSee('Auto Selected Team')
|
||||
->assertDontSee('admin');
|
||||
});
|
||||
|
||||
it('shows when flux is unavailable', function () {
|
||||
|
|
@ -430,9 +413,10 @@
|
|||
->withSession(['currentTeam' => $team])
|
||||
->get('/v5')
|
||||
->assertSuccessful()
|
||||
->assertSee('"host":"192.0.2.10"', false)
|
||||
->assertSee('"status":"installed"', false)
|
||||
->assertSee('"capabilities":["coold","builder"]', false);
|
||||
->assertDontSee('"host":"192.0.2.10"', false)
|
||||
->assertDontSee('"capabilities":["coold","builder"]', false);
|
||||
|
||||
expect(V5Server::query()->where('host', '192.0.2.10')->where('status', 'installed')->exists())->toBeTrue();
|
||||
|
||||
$sshKeyPath = null;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue