woah
This commit is contained in:
parent
50bac2c056
commit
bbcabc8e71
@ -3,11 +3,14 @@
|
|||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use App\Models\ApplicationDeploymentQueue;
|
use App\Models\ApplicationDeploymentQueue;
|
||||||
|
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||||
|
use Illuminate\Foundation\Validation\ValidatesRequests;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Spatie\Activitylog\Models\Activity;
|
use Spatie\Activitylog\Models\Activity;
|
||||||
|
|
||||||
class ApplicationController extends Controller
|
class ApplicationController extends Controller
|
||||||
{
|
{
|
||||||
|
use AuthorizesRequests, ValidatesRequests;
|
||||||
public function configuration()
|
public function configuration()
|
||||||
{
|
{
|
||||||
$project = session('currentTeam')->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first();
|
$project = session('currentTeam')->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first();
|
||||||
|
@ -2,6 +2,9 @@
|
|||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Models\InstanceSettings;
|
||||||
|
use App\Models\Project;
|
||||||
|
use App\Models\Server;
|
||||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||||
use Illuminate\Foundation\Validation\ValidatesRequests;
|
use Illuminate\Foundation\Validation\ValidatesRequests;
|
||||||
use Illuminate\Routing\Controller as BaseController;
|
use Illuminate\Routing\Controller as BaseController;
|
||||||
@ -9,4 +12,32 @@
|
|||||||
class Controller extends BaseController
|
class Controller extends BaseController
|
||||||
{
|
{
|
||||||
use AuthorizesRequests, ValidatesRequests;
|
use AuthorizesRequests, ValidatesRequests;
|
||||||
|
|
||||||
|
public function dashboard()
|
||||||
|
{
|
||||||
|
$projects = Project::ownedByCurrentTeam()->get();
|
||||||
|
$servers = Server::ownedByCurrentTeam()->get();
|
||||||
|
|
||||||
|
$resources = 0;
|
||||||
|
foreach ($projects as $project) {
|
||||||
|
$resources += $project->applications->count();
|
||||||
|
}
|
||||||
|
|
||||||
|
return view('dashboard', [
|
||||||
|
'servers' => $servers->count(),
|
||||||
|
'projects' => $projects->count(),
|
||||||
|
'resources' => $resources,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
public function settings()
|
||||||
|
{
|
||||||
|
if (auth()->user()->isAdmin()) {
|
||||||
|
$settings = InstanceSettings::get();
|
||||||
|
return view('settings', [
|
||||||
|
'settings' => $settings
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
return redirect()->route('dashboard');
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
57
app/Http/Controllers/MagicController.php
Normal file
57
app/Http/Controllers/MagicController.php
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Http\Livewire\Server\PrivateKey;
|
||||||
|
use App\Models\Environment;
|
||||||
|
use App\Models\Project;
|
||||||
|
use App\Models\Server;
|
||||||
|
|
||||||
|
class MagicController extends Controller
|
||||||
|
{
|
||||||
|
public function servers()
|
||||||
|
{
|
||||||
|
return response()->json([
|
||||||
|
'servers' => Server::validated()->get()
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
public function destinations()
|
||||||
|
{
|
||||||
|
return response()->json([
|
||||||
|
'destinations' => Server::destinationsByServer(request()->query('server_id'))->sortBy('name')
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
public function projects()
|
||||||
|
{
|
||||||
|
return response()->json([
|
||||||
|
'projects' => Project::ownedByCurrentTeam()->get()
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
public function environments()
|
||||||
|
{
|
||||||
|
return response()->json([
|
||||||
|
'environments' => Project::ownedByCurrentTeam()->whereUuid(request()->query('project_uuid'))->first()->environments
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
public function new_project()
|
||||||
|
{
|
||||||
|
$project = Project::firstOrCreate(
|
||||||
|
['name' => request()->query('name') ?? generate_random_name()],
|
||||||
|
['team_id' => session('currentTeam')->id]
|
||||||
|
);
|
||||||
|
ray($project);
|
||||||
|
return response()->json([
|
||||||
|
'project_uuid' => $project->uuid
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
public function new_environment()
|
||||||
|
{
|
||||||
|
$environment = Environment::firstOrCreate(
|
||||||
|
['name' => request()->query('name') ?? generate_random_name()],
|
||||||
|
['project_id' => Project::ownedByCurrentTeam()->whereUuid(request()->query('project_uuid'))->firstOrFail()->id]
|
||||||
|
);
|
||||||
|
return response()->json([
|
||||||
|
'environment_name' => $environment->name,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
34
app/Http/Controllers/ServerController.php
Normal file
34
app/Http/Controllers/ServerController.php
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Models\PrivateKey;
|
||||||
|
use App\Models\Server;
|
||||||
|
|
||||||
|
class ServerController extends Controller
|
||||||
|
{
|
||||||
|
public function all()
|
||||||
|
{
|
||||||
|
return view('server.all', [
|
||||||
|
'servers' => Server::ownedByCurrentTeam()->get()
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
public function create()
|
||||||
|
{
|
||||||
|
return view('server.create', [
|
||||||
|
'private_keys' => PrivateKey::ownedByCurrentTeam()->get(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
public function show()
|
||||||
|
{
|
||||||
|
return view('server.show', [
|
||||||
|
'server' => Server::ownedByCurrentTeam()->whereUuid(request()->server_uuid)->firstOrFail(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
public function proxy()
|
||||||
|
{
|
||||||
|
return view('server.proxy', [
|
||||||
|
'server' => Server::ownedByCurrentTeam()->whereUuid(request()->server_uuid)->firstOrFail(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
@ -26,7 +26,7 @@ public function createPrivateKey()
|
|||||||
'team_id' => session('currentTeam')->id
|
'team_id' => session('currentTeam')->id
|
||||||
]);
|
]);
|
||||||
if ($this->from === 'server') {
|
if ($this->from === 'server') {
|
||||||
return redirect()->route('server.new');
|
return redirect()->route('server.create');
|
||||||
}
|
}
|
||||||
return redirect()->route('private-key.show', ['private_key_uuid' => $private_key->uuid]);
|
return redirect()->route('private-key.show', ['private_key_uuid' => $private_key->uuid]);
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
|
|
||||||
class PrivateKey extends BaseModel
|
class PrivateKey extends BaseModel
|
||||||
{
|
{
|
||||||
protected $fillable = [
|
protected $fillable = [
|
||||||
@ -10,6 +11,10 @@ class PrivateKey extends BaseModel
|
|||||||
'private_key',
|
'private_key',
|
||||||
'team_id',
|
'team_id',
|
||||||
];
|
];
|
||||||
|
static public function ownedByCurrentTeam()
|
||||||
|
{
|
||||||
|
return PrivateKey::whereTeamId(session('currentTeam')->id);
|
||||||
|
}
|
||||||
public function servers()
|
public function servers()
|
||||||
{
|
{
|
||||||
return $this->hasMany(Server::class);
|
return $this->hasMany(Server::class);
|
||||||
|
@ -22,6 +22,11 @@ protected static function booted()
|
|||||||
'team_id',
|
'team_id',
|
||||||
'project_id'
|
'project_id'
|
||||||
];
|
];
|
||||||
|
static public function ownedByCurrentTeam()
|
||||||
|
{
|
||||||
|
return Project::whereTeamId(session('currentTeam')->id);
|
||||||
|
}
|
||||||
|
|
||||||
public function environments()
|
public function environments()
|
||||||
{
|
{
|
||||||
return $this->hasMany(Environment::class);
|
return $this->hasMany(Environment::class);
|
||||||
|
@ -53,28 +53,21 @@ public function settings()
|
|||||||
{
|
{
|
||||||
return $this->hasOne(ServerSetting::class);
|
return $this->hasOne(ServerSetting::class);
|
||||||
}
|
}
|
||||||
|
static public function ownedByCurrentTeam()
|
||||||
|
{
|
||||||
|
return Server::whereTeamId(session('currentTeam')->id);
|
||||||
|
}
|
||||||
|
|
||||||
static public function validated()
|
static public function validated()
|
||||||
{
|
{
|
||||||
return Server::where('team_id', session('currentTeam')->id)->whereRelation('settings', 'is_validated', true)->get();
|
return Server::ownedByCurrentTeam()->whereRelation('settings', 'is_validated', true);
|
||||||
}
|
}
|
||||||
|
|
||||||
static public function destinations(string|null $server_id = null)
|
static public function destinationsByServer(string $server_id)
|
||||||
{
|
{
|
||||||
if ($server_id) {
|
$server = Server::ownedByCurrentTeam()->get()->where('id', $server_id)->firstOrFail();
|
||||||
$server = Server::where('team_id', session('currentTeam')->id)->where('id', $server_id)->firstOrFail();
|
$standaloneDocker = collect($server->standaloneDockers->all());
|
||||||
$standaloneDocker = collect($server->standaloneDockers->all());
|
$swarmDocker = collect($server->swarmDockers->all());
|
||||||
$swarmDocker = collect($server->swarmDockers->all());
|
return $standaloneDocker->concat($swarmDocker);
|
||||||
return $standaloneDocker->concat($swarmDocker);
|
|
||||||
} else {
|
|
||||||
$servers = Server::where('team_id', session('currentTeam')->id)->get();
|
|
||||||
$standaloneDocker = $servers->map(function ($server) {
|
|
||||||
return $server->standaloneDockers;
|
|
||||||
})->flatten();
|
|
||||||
$swarmDocker = $servers->map(function ($server) {
|
|
||||||
return $server->swarmDockers;
|
|
||||||
})->flatten();
|
|
||||||
return $standaloneDocker->concat($swarmDocker);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,7 @@ protected static function boot()
|
|||||||
$model->uuid = (string) new Cuid2(7);
|
$model->uuid = (string) new Cuid2(7);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
public function isPartOfRootTeam()
|
public function isAdmin()
|
||||||
{
|
{
|
||||||
$found_root_team = auth()->user()->teams->filter(function ($team) {
|
$found_root_team = auth()->user()->teams->filter(function ($team) {
|
||||||
if ($team->id == 0) {
|
if ($team->id == 0) {
|
||||||
|
@ -24,34 +24,48 @@
|
|||||||
d="M9 3.5a5.5 5.5 0 100 11 5.5 5.5 0 000-11zM2 9a7 7 0 1112.452 4.391l3.328 3.329a.75.75 0 11-1.06 1.06l-3.329-3.328A7 7 0 012 9z"
|
d="M9 3.5a5.5 5.5 0 100 11 5.5 5.5 0 000-11zM2 9a7 7 0 1112.452 4.391l3.328 3.329a.75.75 0 11-1.06 1.06l-3.329-3.328A7 7 0 012 9z"
|
||||||
clip-rule="evenodd" />
|
clip-rule="evenodd" />
|
||||||
</svg>
|
</svg>
|
||||||
<input type="text" v-model="search" ref="searchInput"
|
<input type="text" v-model="search" ref="searchInput" @keydown.down="focusNext(magic.length)"
|
||||||
|
@keydown.up="focusPrev(magic.length)" @keyup.enter="callAction"
|
||||||
class="w-full h-10 pr-4 text-white rounded outline-none bg-coolgray-400 pl-11 placeholder:text-neutral-700 sm:text-sm focus:outline-none"
|
class="w-full h-10 pr-4 text-white rounded outline-none bg-coolgray-400 pl-11 placeholder:text-neutral-700 sm:text-sm focus:outline-none"
|
||||||
placeholder="Search, jump or create... magically... 🪄" role="combobox"
|
placeholder="Search, jump or create... magically... 🪄" role="combobox"
|
||||||
aria-expanded="false" aria-controls="options">
|
aria-expanded="false" aria-controls="options">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ul class="px-4 pb-2 overflow-y-auto max-h-80 scroll-py-10 scroll-pb-2 scrollbar" id="options"
|
<ul class="px-4 pb-2 overflow-y-auto lg:max-h-screen max-h-80 scroll-py-10 scroll-pb-2 scrollbar"
|
||||||
role="listbox">
|
id="options" role="listbox">
|
||||||
<li v-if="state.showNew">
|
<li v-if="sequenceState.sequence.length !== 0">
|
||||||
|
<h2 v-if="sequenceState.sequence[sequenceState.currentActionIndex] && possibleSequences[sequenceState.sequence[sequenceState.currentActionIndex]]"
|
||||||
|
class="mt-4 mb-2 text-xs font-semibold text-neutral-500">{{
|
||||||
|
possibleSequences[sequenceState.sequence[sequenceState.currentActionIndex]].newTitle }}
|
||||||
|
</h2>
|
||||||
<ul class="mt-2 -mx-4 text-sm text-white ">
|
<ul class="mt-2 -mx-4 text-sm text-white ">
|
||||||
<li class="flex items-center px-4 py-2 cursor-pointer select-none group hover:bg-coolgray-400"
|
<li class="flex items-center px-4 py-2 cursor-pointer select-none group hover:bg-coolgray-400"
|
||||||
id="option-1" role="option" tabindex="-1" @click="next('redirect', -1, state.icon)">
|
id="option-1" role="option" tabindex="-1"
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6" viewBox="0 0 24 24"
|
@click="addNew(sequenceState.sequence[sequenceState.currentActionIndex])">
|
||||||
|
<svg xmlns=" http://www.w3.org/2000/svg" class="w-6 h-6" viewBox="0 0 24 24"
|
||||||
stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round"
|
stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round"
|
||||||
stroke-linejoin="round">
|
stroke-linejoin="round">
|
||||||
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||||
<path d="M12 5l0 14" />
|
<path d="M12 5l0 14" />
|
||||||
<path d="M5 12l14 0" />
|
<path d="M5 12l14 0" />
|
||||||
</svg>
|
</svg>
|
||||||
<span class="flex-auto ml-3 truncate">Add new {{ state.icon }}: <span
|
<span class="flex-auto ml-3 truncate">
|
||||||
class="text-xs text-warning" v-if="search">{{ search }}</span>
|
<span v-if="search"><span class="capitalize ">{{
|
||||||
<span v-else class="text-xs text-warning">with random name (or type
|
sequenceState.sequence[sequenceState.currentActionIndex] }}</span> name
|
||||||
one)</span></span>
|
will be:
|
||||||
|
<span class="text-warning">{{ search
|
||||||
|
}}</span></span>
|
||||||
|
<span v-else><span class="capitalize ">{{
|
||||||
|
sequenceState.sequence[sequenceState.currentActionIndex] }}</span> name
|
||||||
|
will be:
|
||||||
|
<span class="text-warning">randomly generated (type to change)</span>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<ul v-if="data.length == 0" class="mt-2 -mx-4 text-sm text-white">
|
<ul v-if="magic.length == 0" class="mt-2 -mx-4 text-sm text-white">
|
||||||
<li class="flex items-center px-4 py-2 select-none group">
|
<li class="flex items-center px-4 py-2 select-none group">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6 icon" viewBox="0 0 24 24"
|
<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6 icon" viewBox="0 0 24 24"
|
||||||
stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round"
|
stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round"
|
||||||
@ -65,67 +79,94 @@
|
|||||||
<span class="flex-auto ml-3 truncate">Nothing found. Ooops.</span>
|
<span class="flex-auto ml-3 truncate">Nothing found. Ooops.</span>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<h2 v-if="data.length != 0 && state.title"
|
<h2 v-if="magic.length !== 0 && sequenceState.sequence[sequenceState.currentActionIndex] && possibleSequences[sequenceState.sequence[sequenceState.currentActionIndex]]"
|
||||||
class="mt-4 mb-2 text-xs font-semibold text-neutral-500">{{
|
class="mt-4 mb-2 text-xs font-semibold text-neutral-500">{{
|
||||||
state.title }}
|
possibleSequences[sequenceState.sequence[sequenceState.currentActionIndex]].title }}
|
||||||
</h2>
|
</h2>
|
||||||
<ul v-if="data.length != 0" class="mt-2 -mx-4 text-sm text-white">
|
<ul v-if="magic.length != 0" class="mt-2 -mx-4 text-sm text-white">
|
||||||
<li class="flex items-center px-4 py-2 cursor-pointer select-none group hover:bg-coolgray-400"
|
<li class="flex items-center px-4 py-2 transition-all cursor-pointer select-none group hover:bg-coolgray-400"
|
||||||
id="option-1" role="option" tabindex="-1" v-for="action, index in data"
|
:class="{ 'bg-coolgray-400': currentFocus === index }" id="option-1" role="option"
|
||||||
@click="next(state.next ?? action.next, index, action.newAction)">
|
tabindex="-1" v-for="action, index in magic" @click="goThroughSequence(action.id)">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6 icon" viewBox="0 0 24 24"
|
<div class="relative">
|
||||||
stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round"
|
<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6 icon" viewBox="0 0 24 24"
|
||||||
stroke-linejoin="round">
|
stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round"
|
||||||
<template v-if="action.icon === 'git' || state.icon === 'git'">
|
stroke-linejoin="round">
|
||||||
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
<template
|
||||||
<path d="M16 12m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0" />
|
v-if="action.icon === 'git' || sequenceState.sequence[sequenceState.currentActionIndex] === 'git'">
|
||||||
<path d="M12 8m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0" />
|
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||||
<path d="M12 16m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0" />
|
<path d="M16 12m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0" />
|
||||||
<path d="M12 15v-6" />
|
<path d="M12 8m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0" />
|
||||||
<path d="M15 11l-2 -2" />
|
<path d="M12 16m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0" />
|
||||||
<path d="M11 7l-1.9 -1.9" />
|
<path d="M12 15v-6" />
|
||||||
<path
|
<path d="M15 11l-2 -2" />
|
||||||
d="M13.446 2.6l7.955 7.954a2.045 2.045 0 0 1 0 2.892l-7.955 7.955a2.045 2.045 0 0 1 -2.892 0l-7.955 -7.955a2.045 2.045 0 0 1 0 -2.892l7.955 -7.955a2.045 2.045 0 0 1 2.892 0z" />
|
<path d="M11 7l-1.9 -1.9" />
|
||||||
</template>
|
<path
|
||||||
<template v-if="action.icon === 'server' || state.icon === 'server'">
|
d="M13.446 2.6l7.955 7.954a2.045 2.045 0 0 1 0 2.892l-7.955 7.955a2.045 2.045 0 0 1 -2.892 0l-7.955 -7.955a2.045 2.045 0 0 1 0 -2.892l7.955 -7.955a2.045 2.045 0 0 1 2.892 0z" />
|
||||||
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
</template>
|
||||||
<path
|
<template
|
||||||
d="M3 4m0 3a3 3 0 0 1 3 -3h12a3 3 0 0 1 3 3v2a3 3 0 0 1 -3 3h-12a3 3 0 0 1 -3 -3z" />
|
v-if="action.icon === 'server' || sequenceState.sequence[sequenceState.currentActionIndex] === 'server'">
|
||||||
<path d="M15 20h-9a3 3 0 0 1 -3 -3v-2a3 3 0 0 1 3 -3h12" />
|
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||||
<path d="M7 8v.01" />
|
<path
|
||||||
<path d="M7 16v.01" />
|
d="M3 4m0 3a3 3 0 0 1 3 -3h12a3 3 0 0 1 3 3v2a3 3 0 0 1 -3 3h-12a3 3 0 0 1 -3 -3z" />
|
||||||
<path d="M20 15l-2 3h3l-2 3" />
|
<path d="M15 20h-9a3 3 0 0 1 -3 -3v-2a3 3 0 0 1 3 -3h12" />
|
||||||
</template>
|
<path d="M7 8v.01" />
|
||||||
<template v-if="action.icon === 'destination' || state.icon === 'destination'">
|
<path d="M7 16v.01" />
|
||||||
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
<path d="M20 15l-2 3h3l-2 3" />
|
||||||
<path
|
</template>
|
||||||
d="M22 12.54c-1.804 -.345 -2.701 -1.08 -3.523 -2.94c-.487 .696 -1.102 1.568 -.92 2.4c.028 .238 -.32 1 -.557 1h-14c0 5.208 3.164 7 6.196 7c4.124 .022 7.828 -1.376 9.854 -5c1.146 -.101 2.296 -1.505 2.95 -2.46z" />
|
<template
|
||||||
<path d="M5 10h3v3h-3z" />
|
v-if="action.icon === 'destination' || sequenceState.sequence[sequenceState.currentActionIndex] === 'destination'">
|
||||||
<path d="M8 10h3v3h-3z" />
|
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||||
<path d="M11 10h3v3h-3z" />
|
<path
|
||||||
<path d="M8 7h3v3h-3z" />
|
d="M22 12.54c-1.804 -.345 -2.701 -1.08 -3.523 -2.94c-.487 .696 -1.102 1.568 -.92 2.4c.028 .238 -.32 1 -.557 1h-14c0 5.208 3.164 7 6.196 7c4.124 .022 7.828 -1.376 9.854 -5c1.146 -.101 2.296 -1.505 2.95 -2.46z" />
|
||||||
<path d="M11 7h3v3h-3z" />
|
<path d="M5 10h3v3h-3z" />
|
||||||
<path d="M11 4h3v3h-3z" />
|
<path d="M8 10h3v3h-3z" />
|
||||||
<path d="M4.571 18c1.5 0 2.047 -.074 2.958 -.78" />
|
<path d="M11 10h3v3h-3z" />
|
||||||
<path d="M10 16l0 .01" />
|
<path d="M8 7h3v3h-3z" />
|
||||||
</template>
|
<path d="M11 7h3v3h-3z" />
|
||||||
<template v-if="action.icon === 'project' || state.icon === 'project'">
|
<path d="M11 4h3v3h-3z" />
|
||||||
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
<path d="M4.571 18c1.5 0 2.047 -.074 2.958 -.78" />
|
||||||
<path
|
<path d="M10 16l0 .01" />
|
||||||
d="M9 4h3l2 2h5a2 2 0 0 1 2 2v7a2 2 0 0 1 -2 2h-10a2 2 0 0 1 -2 -2v-9a2 2 0 0 1 2 -2" />
|
</template>
|
||||||
<path d="M17 17v2a2 2 0 0 1 -2 2h-10a2 2 0 0 1 -2 -2v-9a2 2 0 0 1 2 -2h2" />
|
<template
|
||||||
</template>
|
v-if="action.icon === 'project' || sequenceState.sequence[sequenceState.currentActionIndex] === 'project'">
|
||||||
<template v-if="action.icon === 'environment' || state.icon === 'environment'">
|
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||||
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
<path
|
||||||
<path d="M16 5l3 3l-2 1l4 4l-3 1l4 4h-9" />
|
d="M9 4h3l2 2h5a2 2 0 0 1 2 2v7a2 2 0 0 1 -2 2h-10a2 2 0 0 1 -2 -2v-9a2 2 0 0 1 2 -2" />
|
||||||
<path d="M15 21l0 -3" />
|
<path
|
||||||
<path d="M8 13l-2 -2" />
|
d="M17 17v2a2 2 0 0 1 -2 2h-10a2 2 0 0 1 -2 -2v-9a2 2 0 0 1 2 -2h2" />
|
||||||
<path d="M8 12l2 -2" />
|
</template>
|
||||||
<path d="M8 21v-13" />
|
<template
|
||||||
<path
|
v-if="action.icon === 'environment' || sequenceState.sequence[sequenceState.currentActionIndex] === 'environment'">
|
||||||
d="M5.824 16a3 3 0 0 1 -2.743 -3.69a3 3 0 0 1 .304 -4.833a3 3 0 0 1 4.615 -3.707a3 3 0 0 1 4.614 3.707a3 3 0 0 1 .305 4.833a3 3 0 0 1 -2.919 3.695h-4z" />
|
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||||
</template>
|
<path d="M16 5l3 3l-2 1l4 4l-3 1l4 4h-9" />
|
||||||
</svg>
|
<path d="M15 21l0 -3" />
|
||||||
|
<path d="M8 13l-2 -2" />
|
||||||
|
<path d="M8 12l2 -2" />
|
||||||
|
<path d="M8 21v-13" />
|
||||||
|
<path
|
||||||
|
d="M5.824 16a3 3 0 0 1 -2.743 -3.69a3 3 0 0 1 .304 -4.833a3 3 0 0 1 4.615 -3.707a3 3 0 0 1 4.614 3.707a3 3 0 0 1 .305 4.833a3 3 0 0 1 -2.919 3.695h-4z" />
|
||||||
|
</template>
|
||||||
|
<template
|
||||||
|
v-if="action.icon === 'key' || sequenceState.sequence[sequenceState.currentActionIndex] === 'key'">
|
||||||
|
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||||
|
<path d="M14 10m-2 0a2 2 0 1 0 4 0a2 2 0 1 0 -4 0" />
|
||||||
|
<path d="M21 12a9 9 0 1 1 -18 0a9 9 0 0 1 18 0z" />
|
||||||
|
<path d="M12.5 11.5l-4 4l1.5 1.5" />
|
||||||
|
<path d="M12 15l-1.5 -1.5" />
|
||||||
|
</template>
|
||||||
|
<template
|
||||||
|
v-if="action.icon === 'goto' || sequenceState.sequence[sequenceState.currentActionIndex] === 'goto'">
|
||||||
|
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||||
|
<path d="M10 18h4" />
|
||||||
|
<path
|
||||||
|
d="M3 8a9 9 0 0 1 9 9v1l1.428 -4.285a12 12 0 0 1 6.018 -6.938l.554 -.277" />
|
||||||
|
<path d="M15 6h5v5" />
|
||||||
|
</template>
|
||||||
|
</svg>
|
||||||
|
<div v-if="action.new"
|
||||||
|
class="absolute top-0 right-0 -mt-2 -mr-2 font-bold text-warning">+
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<span class="flex-auto ml-3 truncate">{{ action.name }}</span>
|
<span class="flex-auto ml-3 truncate">{{ action.name }}</span>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
@ -140,51 +181,140 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { ref, computed, onMounted, onUnmounted, watch, nextTick } from 'vue'
|
import { ref, computed, onMounted, onUnmounted, watch, nextTick } from 'vue'
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
|
const currentFocus = ref()
|
||||||
|
function focusNext(length) {
|
||||||
|
if (currentFocus.value === undefined) {
|
||||||
|
currentFocus.value = 0
|
||||||
|
} else {
|
||||||
|
if (length > currentFocus.value + 1) {
|
||||||
|
currentFocus.value = currentFocus.value + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function focusPrev(length) {
|
||||||
|
if (currentFocus.value === undefined) {
|
||||||
|
currentFocus.value = length - 1
|
||||||
|
} else {
|
||||||
|
if (currentFocus.value > 0) {
|
||||||
|
currentFocus.value = currentFocus.value - 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async function callAction() {
|
||||||
|
await goThroughSequence(currentFocus.value)
|
||||||
|
}
|
||||||
const showCommandPalette = ref(false)
|
const showCommandPalette = ref(false)
|
||||||
const search = ref()
|
const search = ref()
|
||||||
const searchInput = ref()
|
const searchInput = ref()
|
||||||
const baseUrl = '/magic'
|
|
||||||
let selected = {};
|
|
||||||
|
|
||||||
const appActions = [{
|
const baseUrl = '/magic'
|
||||||
|
|
||||||
|
const uuidSelector = ['project', 'destination']
|
||||||
|
const nameSelector = ['environment']
|
||||||
|
const possibleSequences = {
|
||||||
|
server: {
|
||||||
|
newTitle: 'Add a new server',
|
||||||
|
title: 'Select a server'
|
||||||
|
},
|
||||||
|
destination: {
|
||||||
|
newTitle: 'Add a new destination',
|
||||||
|
title: 'Select a destination'
|
||||||
|
},
|
||||||
|
project: {
|
||||||
|
newTitle: 'Add a new project',
|
||||||
|
title: 'Select a project'
|
||||||
|
},
|
||||||
|
environment: {
|
||||||
|
newTitle: 'Add a new environment',
|
||||||
|
title: 'Select an environment'
|
||||||
|
},
|
||||||
|
}
|
||||||
|
const magicActions = [{
|
||||||
id: 0,
|
id: 0,
|
||||||
name: 'Public Repository',
|
name: 'Deploy a Public Repository',
|
||||||
tags: 'application,public,repository,github,gitlab,bitbucket,git',
|
tags: 'git,github,public',
|
||||||
icon: 'git',
|
icon: 'git',
|
||||||
next: 'server'
|
new: true,
|
||||||
|
sequence: ['main', 'server', 'destination', 'project', 'environment', 'redirect']
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 1,
|
id: 1,
|
||||||
name: 'Private Repository (with GitHub Apps)',
|
name: 'Deploy a Private Repository (with GitHub Apps)',
|
||||||
tags: 'application,private,repository,github,gitlab,bitbucket,git',
|
tags: 'git,github,private',
|
||||||
icon: 'git',
|
icon: 'git',
|
||||||
next: 'server'
|
new: true,
|
||||||
|
sequence: ['main', 'server', 'destination', 'project', 'environment', 'redirect']
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 2,
|
id: 2,
|
||||||
name: 'Private Repository (with Deploy Key)',
|
name: 'Deploy a Private Repository (with Deploy Key)',
|
||||||
tags: 'application,private,repository,github,gitlab,bitbucket,git',
|
tags: 'git,github,private,deploy,key',
|
||||||
icon: 'git',
|
icon: 'git',
|
||||||
next: 'server'
|
new: true,
|
||||||
|
sequence: ['main', 'server', 'destination', 'project', 'environment', 'redirect']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
name: 'Add a Private Key',
|
||||||
|
tags: 'key,private,ssh',
|
||||||
|
icon: 'key',
|
||||||
|
new: true,
|
||||||
|
sequence: ['main', 'redirect']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 4,
|
||||||
|
name: 'Add a Server',
|
||||||
|
tags: 'server,ssh,new,create',
|
||||||
|
icon: 'server',
|
||||||
|
new: true,
|
||||||
|
sequence: ['main', 'redirect']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 5,
|
||||||
|
name: 'Add a Destination',
|
||||||
|
tags: 'destination,docker,network,new,create',
|
||||||
|
icon: 'destination',
|
||||||
|
new: true,
|
||||||
|
sequence: ['main', 'server', 'redirect']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 6,
|
||||||
|
name: 'Goto Dashboard',
|
||||||
|
icon: 'goto',
|
||||||
|
sequence: ['main', 'redirect']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 7,
|
||||||
|
name: 'Goto Servers',
|
||||||
|
icon: 'goto',
|
||||||
|
sequence: ['main', 'redirect']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 8,
|
||||||
|
name: 'Goto Projects',
|
||||||
|
icon: 'goto',
|
||||||
|
sequence: ['main', 'redirect']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 9,
|
||||||
|
name: 'Goto Settings',
|
||||||
|
icon: 'goto',
|
||||||
|
sequence: ['main', 'redirect']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 10,
|
||||||
|
name: 'Goto Command Center',
|
||||||
|
icon: 'goto',
|
||||||
|
sequence: ['main', 'redirect']
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
const initialState = {
|
const initialState = {
|
||||||
title: null,
|
sequence: [],
|
||||||
icon: null,
|
currentActionIndex: 0,
|
||||||
next: null,
|
magicActions,
|
||||||
current: null,
|
selected: {}
|
||||||
showNew: false,
|
|
||||||
data: appActions
|
|
||||||
}
|
}
|
||||||
const state = ref({ ...initialState })
|
const sequenceState = ref({ ...initialState })
|
||||||
|
|
||||||
const data = computed(() => {
|
|
||||||
if (search?.value) {
|
|
||||||
return state.value.data.filter(item => item.name.toLowerCase().includes(search.value?.toLowerCase() ?? ''))
|
|
||||||
}
|
|
||||||
return state.value.data
|
|
||||||
})
|
|
||||||
|
|
||||||
function focusSearch(event) {
|
function focusSearch(event) {
|
||||||
if (event.target.nodeName === 'BODY') {
|
if (event.target.nodeName === 'BODY') {
|
||||||
@ -208,135 +338,166 @@ watch(showCommandPalette, async (value) => {
|
|||||||
searchInput.value.focus();
|
searchInput.value.focus();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
const magic = computed(() => {
|
||||||
|
if (search.value) {
|
||||||
|
return sequenceState.value.magicActions.filter(action => {
|
||||||
|
return action.name.toLowerCase().includes(search.value.toLowerCase()) || action.tags?.toLowerCase().includes(search.value.toLowerCase())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return sequenceState.value.magicActions
|
||||||
|
})
|
||||||
|
async function addNew(name) {
|
||||||
|
let targetUrl = new URL(window.location.origin)
|
||||||
|
let newUrl = new URL(`${window.location.origin}${baseUrl}/${name}/new`);
|
||||||
|
if (search.value) {
|
||||||
|
targetUrl.searchParams.append('name', search.value)
|
||||||
|
newUrl.searchParams.append('name', search.value)
|
||||||
|
}
|
||||||
|
switch (name) {
|
||||||
|
case 'server':
|
||||||
|
targetUrl.pathname = '/server/new'
|
||||||
|
window.location.href = targetUrl.href
|
||||||
|
break;
|
||||||
|
case 'destination':
|
||||||
|
targetUrl.pathname = '/destination/new'
|
||||||
|
window.location.href = targetUrl.href
|
||||||
|
break;
|
||||||
|
case 'project':
|
||||||
|
const { data: { project_uuid } } = await axios(newUrl.href)
|
||||||
|
search.value = ''
|
||||||
|
sequenceState.value.selected['project'] = project_uuid
|
||||||
|
sequenceState.value.magicActions = await getEnvironments(project_uuid)
|
||||||
|
sequenceState.value.currentActionIndex += 1
|
||||||
|
break;
|
||||||
|
case 'environment':
|
||||||
|
newUrl.searchParams.append('project_uuid', sequenceState.value.selected.project)
|
||||||
|
const { data: { environment_name } } = await axios(newUrl.href)
|
||||||
|
search.value = ''
|
||||||
|
sequenceState.value.selected['environment'] = environment_name
|
||||||
|
redirect()
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
function resetState() {
|
function resetState() {
|
||||||
showCommandPalette.value = false
|
showCommandPalette.value = false
|
||||||
state.value = { ...initialState }
|
sequenceState.value = { ...initialState }
|
||||||
selected = {}
|
|
||||||
search.value = ''
|
search.value = ''
|
||||||
}
|
}
|
||||||
async function next(nextAction, index, newAction = null) {
|
async function goThroughSequence(actionId) {
|
||||||
console.log({ nextAction, index, newAction })
|
let currentSequence = null;
|
||||||
if (newAction) {
|
let nextSequence = null;
|
||||||
let targetUrl = new URL(window.location.origin)
|
if (sequenceState.value.selected.main === undefined) {
|
||||||
let newUrl = new URL(`${window.location.origin}${baseUrl}/${newAction}/new`);
|
const { sequence, id } = magic.value[actionId];
|
||||||
if (search.value) newUrl.searchParams.append('name', search.value)
|
currentSequence = sequence[sequenceState.value.currentActionIndex]
|
||||||
switch (newAction) {
|
nextSequence = sequence[sequenceState.value.currentActionIndex + 1]
|
||||||
case 'server':
|
sequenceState.value.sequence = sequence
|
||||||
targetUrl.pathname = '/server/new'
|
sequenceState.value.selected = {
|
||||||
window.location.href = targetUrl.href
|
main: id
|
||||||
break;
|
|
||||||
case 'destination':
|
|
||||||
targetUrl.pathname = '/destination/new'
|
|
||||||
window.location.href = targetUrl.href
|
|
||||||
break;
|
|
||||||
case 'project':
|
|
||||||
const { data: { new_project_uuid, new_project_id } } = await axios(newUrl.href)
|
|
||||||
selected.project = new_project_uuid
|
|
||||||
await getEnvironments(new_project_id)
|
|
||||||
state.value.title = 'Select an Environment'
|
|
||||||
state.value.icon = 'environment'
|
|
||||||
break;
|
|
||||||
case 'environment':
|
|
||||||
if (selected.project) newUrl.searchParams.append('project_uuid', selected.project)
|
|
||||||
const { data: { new_environment_name } } = await axios(newUrl.href)
|
|
||||||
selected.environment = new_environment_name
|
|
||||||
await redirect();
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
if (state.value.current) {
|
currentSequence = sequenceState.value.sequence[sequenceState.value.currentActionIndex]
|
||||||
if (state.value.current === 'environment') {
|
nextSequence = sequenceState.value.sequence[sequenceState.value.currentActionIndex + 1]
|
||||||
selected[state.value.current] = state.value.data[index].name
|
let selectedId = sequenceState.value.magicActions[actionId].id
|
||||||
} else {
|
if (uuidSelector.includes(currentSequence)) {
|
||||||
selected[state.value.current] = state.value.data[index].uuid
|
selectedId = sequenceState.value.magicActions[actionId].uuid
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else selected['action'] = appActions[index].id
|
if (nameSelector.includes(currentSequence)) {
|
||||||
console.log({ selected })
|
selectedId = sequenceState.value.magicActions[actionId].name
|
||||||
switch (nextAction) {
|
}
|
||||||
case 'server':
|
sequenceState.value.selected = {
|
||||||
await getServers()
|
...sequenceState.value.selected,
|
||||||
state.value.title = 'Select a server'
|
[currentSequence]: selectedId
|
||||||
state.value.icon = 'server'
|
|
||||||
state.value.showNew = true
|
|
||||||
break;
|
|
||||||
case 'destination':
|
|
||||||
await getDestinations(state.value.data[index].id)
|
|
||||||
state.value.title = 'Select a destination'
|
|
||||||
state.value.icon = 'destination'
|
|
||||||
state.value.showNew = true
|
|
||||||
break;
|
|
||||||
case 'project':
|
|
||||||
await getProjects()
|
|
||||||
state.value.title = 'Select a project'
|
|
||||||
state.value.icon = 'project'
|
|
||||||
state.value.showNew = true
|
|
||||||
break;
|
|
||||||
case 'environment':
|
|
||||||
await getEnvironments(state.value.data[index].id)
|
|
||||||
state.value.title = 'Select an environment'
|
|
||||||
state.value.icon = 'environment'
|
|
||||||
state.value.showNew = true
|
|
||||||
break;
|
|
||||||
case 'redirect':
|
|
||||||
await redirect();
|
|
||||||
state.value.showNew = false
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
switch (nextSequence) {
|
||||||
|
case 'server':
|
||||||
|
sequenceState.value.magicActions = await getServers();
|
||||||
|
break;
|
||||||
|
case 'destination':
|
||||||
|
sequenceState.value.magicActions = await getDestinations(sequenceState.value.selected[currentSequence]);
|
||||||
|
break;
|
||||||
|
case 'project':
|
||||||
|
sequenceState.value.magicActions = await getProjects()
|
||||||
|
break;
|
||||||
|
case 'environment':
|
||||||
|
sequenceState.value.magicActions = await getEnvironments(sequenceState.value.selected[currentSequence])
|
||||||
|
break;
|
||||||
|
case 'redirect':
|
||||||
|
redirect()
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
sequenceState.value.currentActionIndex += 1
|
||||||
search.value = ''
|
search.value = ''
|
||||||
searchInput.value.focus()
|
searchInput.value.focus()
|
||||||
}
|
}
|
||||||
|
async function getServers() {
|
||||||
|
const { data: { servers } } = await axios.get(`${baseUrl}/servers`);
|
||||||
|
return servers;
|
||||||
|
}
|
||||||
|
async function getDestinations(serverId) {
|
||||||
|
const { data: { destinations } } = await axios.get(`${baseUrl}/destinations?server_id=${serverId}`);
|
||||||
|
return destinations;
|
||||||
|
}
|
||||||
|
async function getProjects() {
|
||||||
|
const { data: { projects } } = await axios.get(`${baseUrl}/projects`);
|
||||||
|
return projects;
|
||||||
|
}
|
||||||
|
async function getEnvironments(project_uuid) {
|
||||||
|
const { data: { environments } } = await axios.get(`${baseUrl}/environments?project_uuid=${project_uuid}`);
|
||||||
|
return environments;
|
||||||
|
}
|
||||||
|
|
||||||
async function redirect() {
|
async function redirect() {
|
||||||
let targetUrl = new URL(window.location.origin)
|
let targetUrl = new URL(window.location.origin)
|
||||||
switch (selected.action) {
|
const selected = sequenceState.value.selected
|
||||||
|
const { main, destination = null, project = null, environment = null, server = null } = selected
|
||||||
|
switch (main) {
|
||||||
case 0:
|
case 0:
|
||||||
targetUrl.pathname = `/project/${selected.project}/${selected.environment}/new`
|
targetUrl.pathname = `/project/${project}/${environment}/new`
|
||||||
targetUrl.searchParams.append('type', 'public')
|
targetUrl.searchParams.append('type', 'public')
|
||||||
targetUrl.searchParams.append('destination', selected.destination)
|
targetUrl.searchParams.append('destination', destination)
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
targetUrl.pathname = `/project/${selected.project}/${selected.environment}/new`
|
targetUrl.pathname = `/project/${project}/${environment}/new`
|
||||||
targetUrl.searchParams.append('type', 'private-gh-app')
|
targetUrl.searchParams.append('type', 'private-gh-app')
|
||||||
targetUrl.searchParams.append('destination', selected.destination)
|
targetUrl.searchParams.append('destination', destination)
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
targetUrl.pathname = `/project/${selected.project}/${selected.environment}/new`
|
targetUrl.pathname = `/project/${project}/${environment}/new`
|
||||||
targetUrl.searchParams.append('type', 'private-deploy-key')
|
targetUrl.searchParams.append('type', 'private-deploy-key')
|
||||||
targetUrl.searchParams.append('destination', selected.destination)
|
targetUrl.searchParams.append('destination', destination)
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
targetUrl.pathname = `/server/${selected.server}/`
|
targetUrl.pathname = `/private-key/new/`
|
||||||
break;
|
break;
|
||||||
|
case 4:
|
||||||
|
targetUrl.pathname = `/server/new/`
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
targetUrl.pathname = `/destination/new/`
|
||||||
|
targetUrl.searchParams.append('server', server)
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
targetUrl.pathname = `/`
|
||||||
|
break;
|
||||||
|
case 7:
|
||||||
|
targetUrl.pathname = `/servers`
|
||||||
|
break;
|
||||||
|
case 8:
|
||||||
|
targetUrl.pathname = `/projects`
|
||||||
|
break;
|
||||||
|
case 9:
|
||||||
|
targetUrl.pathname = `/settings`
|
||||||
|
break;
|
||||||
|
case 10:
|
||||||
|
targetUrl.pathname = `/command-center`
|
||||||
|
break;
|
||||||
|
|
||||||
}
|
}
|
||||||
window.location.href = targetUrl;
|
window.location.href = targetUrl;
|
||||||
}
|
}
|
||||||
async function getServers() {
|
|
||||||
const { data } = await axios.get(`${baseUrl}/servers`);
|
|
||||||
state.value.data = data.servers
|
|
||||||
state.value.current = 'server'
|
|
||||||
state.value.next = 'destination'
|
|
||||||
}
|
|
||||||
async function getDestinations(serverId) {
|
|
||||||
const { data } = await axios.get(`${baseUrl}/destinations?server_id=${serverId}`);
|
|
||||||
state.value.data = data.destinations
|
|
||||||
state.value.current = 'destination'
|
|
||||||
state.value.next = 'project'
|
|
||||||
}
|
|
||||||
async function getProjects() {
|
|
||||||
const { data } = await axios.get(`${baseUrl}/projects`);
|
|
||||||
state.value.data = data.projects
|
|
||||||
state.value.current = 'project'
|
|
||||||
state.value.next = 'environment'
|
|
||||||
}
|
|
||||||
async function getEnvironments(projectId) {
|
|
||||||
const { data } = await axios.get(`${baseUrl}/environments?project_id=${projectId}`);
|
|
||||||
state.value.data = data.environments
|
|
||||||
state.value.current = 'environment'
|
|
||||||
state.value.next = 'redirect'
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
@ -1,8 +1,17 @@
|
|||||||
<x-layout>
|
<x-layout>
|
||||||
<h1 class="pb-2">Command Center</h1>
|
<h1>Command Center</h1>
|
||||||
|
<div class="pb-10 text-sm breadcrumbs">
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
Execute commands on your servers without leaving the browser.
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
@if ($servers->count() > 0)
|
@if ($servers->count() > 0)
|
||||||
<livewire:run-command :servers="$servers" />
|
<livewire:run-command :servers="$servers" />
|
||||||
@else
|
@else
|
||||||
<div>No validated servers found.</div>
|
<div>No validated servers found.
|
||||||
|
<x-use-magic-bar />
|
||||||
|
</div>
|
||||||
@endif
|
@endif
|
||||||
</x-layout>
|
</x-layout>
|
||||||
|
@ -41,7 +41,7 @@ class="{{ request()->is('project/*') || request()->is('projects') ? 'text-warnin
|
|||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
|
||||||
@if (auth()->user()->isPartOfRootTeam())
|
@if (auth()->user()->isAdmin())
|
||||||
<li title="Command Center">
|
<li title="Command Center">
|
||||||
<a class="hover:bg-transparent" @if (!request()->is('command-center')) href="/command-center" @endif>
|
<a class="hover:bg-transparent" @if (!request()->is('command-center')) href="/command-center" @endif>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg"
|
<svg xmlns="http://www.w3.org/2000/svg"
|
||||||
|
2
resources/views/components/use-magic-bar.blade.php
Normal file
2
resources/views/components/use-magic-bar.blade.php
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
Use the magic
|
||||||
|
bar (press <span class="kbd-custom">/</span>) to create a new one.
|
@ -1,7 +1,11 @@
|
|||||||
<x-layout>
|
<x-layout>
|
||||||
<h1 class="pb-2">Dashboard</h1>
|
<h1>Dashboard</h1>
|
||||||
<div class="pb-10 text-sm">
|
<div class="pb-10 text-sm breadcrumbs">
|
||||||
Something (more) useful will be here.
|
<ul>
|
||||||
|
<li>
|
||||||
|
Something (more) useful will be here.
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="w-full rounded shadow stats stats-vertical lg:stats-horizontal">
|
<div class="w-full rounded shadow stats stats-vertical lg:stats-horizontal">
|
||||||
<div class="stat">
|
<div class="stat">
|
||||||
|
@ -1,5 +1,16 @@
|
|||||||
<x-layout>
|
<x-layout>
|
||||||
<div class="flex items-center justify-center">
|
<main class="grid min-h-full px-6 place-items-center lg:px-8">
|
||||||
<div><a class="underline" href="{{ route('dashboard') }}">Go home</a></div>
|
<div class="text-center">
|
||||||
</div>
|
<p class="text-6xl font-semibold text-warning">404</p>
|
||||||
|
<h1 class="mt-4 text-3xl font-bold tracking-tight text-white sm:text-5xl">Page not found</h1>
|
||||||
|
<p class="mt-6 text-base leading-7 text-neutral-300">Sorry, we couldn’t find the page you’re looking for.</p>
|
||||||
|
<div class="flex items-center justify-center mt-10 gap-x-6">
|
||||||
|
<a href="/"
|
||||||
|
class="rounded-md bg-coollabs px-3.5 py-2.5 text-sm font-semibold text-white shadow-sm hover:bg-coollabs-100 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600 hover:no-underline">Go
|
||||||
|
back home</a>
|
||||||
|
<a href="https://docs.coollabs.io/contact.html" class="text-sm font-semibold text-white">Contact support
|
||||||
|
<span aria-hidden="true">→</span></a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
</x-layout>
|
</x-layout>
|
||||||
|
@ -2,11 +2,11 @@
|
|||||||
<x-naked-modal show="deleteEnvironment" message='Are you sure you would like to delete this environment?' />
|
<x-naked-modal show="deleteEnvironment" message='Are you sure you would like to delete this environment?' />
|
||||||
@if ($resource_count > 0)
|
@if ($resource_count > 0)
|
||||||
<x-forms.button tooltip="First delete all resources." disabled>
|
<x-forms.button tooltip="First delete all resources." disabled>
|
||||||
Delete
|
Delete Environment
|
||||||
</x-forms.button>
|
</x-forms.button>
|
||||||
@else
|
@else
|
||||||
<x-forms.button x-on:click.prevent="deleteEnvironment = true">
|
<x-forms.button x-on:click.prevent="deleteEnvironment = true">
|
||||||
Delete
|
Delete Environment
|
||||||
</x-forms.button>
|
</x-forms.button>
|
||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
|
@ -2,11 +2,11 @@
|
|||||||
<x-naked-modal show="deleteProject" message='Are you sure you would like to delete this project?' />
|
<x-naked-modal show="deleteProject" message='Are you sure you would like to delete this project?' />
|
||||||
@if ($resource_count > 0)
|
@if ($resource_count > 0)
|
||||||
<x-forms.button disabled="First delete all resources.">
|
<x-forms.button disabled="First delete all resources.">
|
||||||
Delete
|
Delete Project
|
||||||
</x-forms.button>
|
</x-forms.button>
|
||||||
@else
|
@else
|
||||||
<x-forms.button x-on:click.prevent="deleteProject = true">
|
<x-forms.button x-on:click.prevent="deleteProject = true">
|
||||||
Delete
|
Delete Project
|
||||||
</x-forms.button>
|
</x-forms.button>
|
||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
<div>
|
<div>
|
||||||
<div class="pb-4 text-sm">Outputs are not saved at the moment, only available until you refresh or navigate.</div>
|
|
||||||
<form class="flex items-end justify-center gap-2" wire:submit.prevent='runCommand'>
|
<form class="flex items-end justify-center gap-2" wire:submit.prevent='runCommand'>
|
||||||
<x-forms.input placeholder="ls -l" autofocus noDirty id="command" label="Command" required />
|
<x-forms.input placeholder="ls -l" autofocus noDirty id="command" label="Command" required />
|
||||||
<x-forms.select label="Server" id="server" required>
|
<x-forms.select label="Server" id="server" required>
|
||||||
|
@ -9,9 +9,16 @@
|
|||||||
Delete
|
Delete
|
||||||
</x-forms.button>
|
</x-forms.button>
|
||||||
@endif
|
@endif
|
||||||
|
@if (!$server->settings->is_validated)
|
||||||
|
<div class="w-full">
|
||||||
|
<x-forms.button isHighlighted wire:click.prevent='validateServer'>
|
||||||
|
Validate Server
|
||||||
|
</x-forms.button>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col gap-2 xl:flex-row">
|
<div class="flex flex-col gap-2 xl:flex-row">
|
||||||
<div class="flex flex-col w-96">
|
<div class="flex flex-col w-full">
|
||||||
@if ($server->id === 0)
|
@if ($server->id === 0)
|
||||||
<x-forms.input id="server.name" label="Name" readonly required />
|
<x-forms.input id="server.name" label="Name" readonly required />
|
||||||
<x-forms.input id="server.description" label="Description" readonly />
|
<x-forms.input id="server.description" label="Description" readonly />
|
||||||
@ -23,7 +30,7 @@
|
|||||||
{{-- <x-forms.checkbox disabled type="checkbox" id="server.settings.is_part_of_swarm"
|
{{-- <x-forms.checkbox disabled type="checkbox" id="server.settings.is_part_of_swarm"
|
||||||
label="Is it part of a Swarm cluster?" /> --}}
|
label="Is it part of a Swarm cluster?" /> --}}
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-col w-full">
|
||||||
@if ($server->id === 0)
|
@if ($server->id === 0)
|
||||||
<x-forms.input id="server.ip" label="IP Address" readonly required />
|
<x-forms.input id="server.ip" label="IP Address" readonly required />
|
||||||
<x-forms.input id="server.user" label="User" readonly required />
|
<x-forms.input id="server.user" label="User" readonly required />
|
||||||
@ -37,13 +44,7 @@
|
|||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@if (!$server->settings->is_validated)
|
|
||||||
<div class="w-full pt-4">
|
|
||||||
<x-forms.button isHighlighted wire:click.prevent='validateServer'>
|
|
||||||
Validate Server
|
|
||||||
</x-forms.button>
|
|
||||||
</div>
|
|
||||||
@endif
|
|
||||||
@if ($server->settings->is_validated)
|
@if ($server->settings->is_validated)
|
||||||
<h3 class="pt-8 pb-4">Quick Actions</h3>
|
<h3 class="pt-8 pb-4">Quick Actions</h3>
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
@ -53,17 +54,18 @@
|
|||||||
{{-- <x-forms.button wire:click.prevent='installDocker'>Install Docker</x-forms.button> --}}
|
{{-- <x-forms.button wire:click.prevent='installDocker'>Install Docker</x-forms.button> --}}
|
||||||
</div>
|
</div>
|
||||||
@endif
|
@endif
|
||||||
<div class="pt-3 text-sm">
|
@isset($uptime)
|
||||||
@isset($uptime)
|
<h3>Server Info</h3>
|
||||||
|
<div class="pt-3 text-sm">
|
||||||
<p>Uptime: {{ $uptime }}</p>
|
<p>Uptime: {{ $uptime }}</p>
|
||||||
@endisset
|
@isset($dockerVersion)
|
||||||
@isset($dockerVersion)
|
<p>Docker Engine {{ $dockerVersion }}</p>
|
||||||
<p>Docker Engine {{ $dockerVersion }}</p>
|
@endisset
|
||||||
@endisset
|
@isset($dockerComposeVersion)
|
||||||
@isset($dockerComposeVersion)
|
<p>{{ $dockerComposeVersion }}</p>
|
||||||
<p>{{ $dockerComposeVersion }}</p>
|
@endisset
|
||||||
@endisset
|
</div>
|
||||||
</div>
|
@endisset
|
||||||
</form>
|
</form>
|
||||||
<div class="flex items-center gap-2 py-4">
|
<div class="flex items-center gap-2 py-4">
|
||||||
<h3>Private Key</h3>
|
<h3>Private Key</h3>
|
||||||
|
@ -1,11 +1,6 @@
|
|||||||
<div>
|
<div>
|
||||||
<form class="flex flex-col gap-1" wire:submit.prevent='submit'>
|
<form class="flex flex-col gap-1" wire:submit.prevent='submit'>
|
||||||
<div class="flex items-center gap-2">
|
<h1>New Server</h1>
|
||||||
<h1>New Server</h1>
|
|
||||||
<x-forms.button type="submit">
|
|
||||||
Save
|
|
||||||
</x-forms.button>
|
|
||||||
</div>
|
|
||||||
<x-forms.input id="name" label="Name" required />
|
<x-forms.input id="name" label="Name" required />
|
||||||
<x-forms.input id="description" label="Description" />
|
<x-forms.input id="description" label="Description" />
|
||||||
<x-forms.input id="ip" label="IP Address" required
|
<x-forms.input id="ip" label="IP Address" required
|
||||||
@ -24,6 +19,8 @@
|
|||||||
@endforeach
|
@endforeach
|
||||||
</x-forms.select>
|
</x-forms.select>
|
||||||
<x-forms.checkbox instantSave noDirty id="is_part_of_swarm" label="Is it part of a Swarm cluster?" />
|
<x-forms.checkbox instantSave noDirty id="is_part_of_swarm" label="Is it part of a Swarm cluster?" />
|
||||||
|
<x-forms.button type="submit">
|
||||||
|
Save
|
||||||
|
</x-forms.button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
<div class="flex flex-wrap gap-2">
|
<div class="grid grid-cols-2">
|
||||||
@forelse ($private_keys as $private_key)
|
@forelse ($private_keys as $private_key)
|
||||||
<div class="w-64 box">
|
<x-forms.button wire:click='setPrivateKey({{ $private_key->id }})'>{{ $private_key->name }}
|
||||||
<button wire:click='setPrivateKey({{ $private_key->id }})'>{{ $private_key->name }}
|
</x-forms.button>
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
@empty
|
@empty
|
||||||
<p>No private keys found</p>
|
<div>No private keys found.
|
||||||
|
<x-use-magic-bar />
|
||||||
|
</div>
|
||||||
@endforelse
|
@endforelse
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,7 +1,13 @@
|
|||||||
<div>
|
<div>
|
||||||
<form wire:submit.prevent='submit' class="flex flex-col">
|
<form wire:submit.prevent='submit' class="flex flex-col">
|
||||||
<h1>Settings</h1>
|
<h1>Settings</h1>
|
||||||
<div class="pt-2 pb-4 text-sm">Instance wide settings for Coolify. </div>
|
<div class="pb-10 text-sm breadcrumbs">
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
Instance wide settings for Coolify.
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<h3>General</h3>
|
<h3>General</h3>
|
||||||
<x-forms.button type="submit">
|
<x-forms.button type="submit">
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<x-layout>
|
<x-layout>
|
||||||
<h1>New Private Key</h1>
|
<h1>Add Private Key</h1>
|
||||||
<livewire:private-key.create />
|
<livewire:private-key.create />
|
||||||
</x-layout>
|
</x-layout>
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
@if ($environment->applications->count() === 0)
|
@if ($environment->applications->count() === 0)
|
||||||
<p>No resources found.</p>
|
<p>No resources found.</p>
|
||||||
@endif
|
@endif
|
||||||
<div class="flex flex-col gap-2">
|
<div class="grid grid-cols-2 gap-2">
|
||||||
@foreach ($environment->applications->sortBy('name') as $application)
|
@foreach ($environment->applications->sortBy('name') as $application)
|
||||||
<a class="box"
|
<a class="box"
|
||||||
href="{{ route('project.application.configuration', [$project->uuid, $environment->name, $application->uuid]) }}">
|
href="{{ route('project.application.configuration', [$project->uuid, $environment->name, $application->uuid]) }}">
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
<li>{{ $project->name }} </li>
|
<li>{{ $project->name }} </li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col gap-2">
|
<div class="grid grid-cols-2 gap-2">
|
||||||
@forelse ($project->environments as $environment)
|
@forelse ($project->environments as $environment)
|
||||||
<a class="box" href="{{ route('project.resources', [$project->uuid, $environment->name]) }}">
|
<a class="box" href="{{ route('project.resources', [$project->uuid, $environment->name]) }}">
|
||||||
{{ $environment->name }}
|
{{ $environment->name }}
|
||||||
|
@ -7,15 +7,14 @@
|
|||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col gap-2">
|
<div class="grid grid-cols-2 gap-2">
|
||||||
@forelse ($projects as $project)
|
@forelse ($projects as $project)
|
||||||
<a href="{{ route('project.show', ['project_uuid' => data_get($project, 'uuid')]) }}"
|
<a href="{{ route('project.show', ['project_uuid' => data_get($project, 'uuid')]) }}"
|
||||||
class="box">{{ $project->name }}</a>
|
class="box">{{ $project->name }}</a>
|
||||||
@empty
|
@empty
|
||||||
<div>
|
<div>
|
||||||
No project found. Use the magic
|
No project found.
|
||||||
bar (press <span class="kbd-custom">/</span>) to create a new
|
<x-use-magic-bar />
|
||||||
project.
|
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
If you do not have a project yet, just create a resource (application, database, etc.) first, it will
|
If you do not have a project yet, just create a resource (application, database, etc.) first, it will
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<x-layout>
|
<x-layout>
|
||||||
<h1 class="py-0">Servers</h1>
|
<h1>Servers</h1>
|
||||||
<div class="pb-10 text-sm breadcrumbs">
|
<div class="pb-10 text-sm breadcrumbs">
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
@ -7,7 +7,7 @@
|
|||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="grid grid-cols-2">
|
<div class="grid grid-cols-2 gap-2">
|
||||||
@forelse ($servers as $server)
|
@forelse ($servers as $server)
|
||||||
<a class="text-center hover:no-underline box group"
|
<a class="text-center hover:no-underline box group"
|
||||||
href="{{ route('server.show', ['server_uuid' => data_get($server, 'uuid')]) }}">
|
href="{{ route('server.show', ['server_uuid' => data_get($server, 'uuid')]) }}">
|
||||||
@ -21,7 +21,7 @@
|
|||||||
@empty
|
@empty
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-col">
|
||||||
<div>Without a server, you won't be able to do much.</div>
|
<div>Without a server, you won't be able to do much.</div>
|
||||||
<div>Let's <a class="text-lg underline text-warning" href="{{ route('server.new') }}">create</a> your
|
<div>Let's <a class="text-lg underline text-warning" href="{{ route('server.create') }}">create</a> your
|
||||||
first one.</div>
|
first one.</div>
|
||||||
</div>
|
</div>
|
||||||
@endforelse
|
@endforelse
|
@ -1,4 +1,4 @@
|
|||||||
<x-layout>
|
<x-layout>
|
||||||
<h1>Select a private Key</h1>
|
<h1 class="pb-2">Select a private Key</h1>
|
||||||
<livewire:server.private-key />
|
<livewire:server.private-key />
|
||||||
</x-layout>
|
</x-layout>
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
<livewire:settings.email :settings="$settings" />
|
<livewire:settings.email :settings="$settings" />
|
||||||
|
|
||||||
<h3 class='pb-4'>Actions</h3>
|
<h3 class='pb-4'>Actions</h3>
|
||||||
@if (auth()->user()->isPartOfRootTeam())
|
@if (auth()->user()->isAdmin())
|
||||||
<livewire:force-upgrade />
|
<livewire:force-upgrade />
|
||||||
@endif
|
@endif
|
||||||
</x-layout>
|
</x-layout>
|
||||||
|
236
routes/web.php
236
routes/web.php
@ -1,152 +1,71 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
use App\Http\Controllers\ApplicationController;
|
use App\Http\Controllers\ApplicationController;
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Http\Controllers\MagicController;
|
||||||
use App\Http\Controllers\ProjectController;
|
use App\Http\Controllers\ProjectController;
|
||||||
|
use App\Http\Controllers\ServerController;
|
||||||
use App\Models\InstanceSettings;
|
use App\Models\InstanceSettings;
|
||||||
use App\Models\PrivateKey;
|
use App\Models\PrivateKey;
|
||||||
use App\Models\StandaloneDocker;
|
use App\Models\StandaloneDocker;
|
||||||
use App\Models\SwarmDocker;
|
use App\Models\SwarmDocker;
|
||||||
use App\Models\Environment;
|
|
||||||
use App\Models\GithubApp;
|
use App\Models\GithubApp;
|
||||||
use App\Models\Project;
|
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\Route;
|
use Illuminate\Support\Facades\Route;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
/*
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
| Web Routes
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
|
|
|
||||||
| Here is where you can register web routes for your application. These
|
|
||||||
| routes are loaded by the RouteServiceProvider and all of them will
|
|
||||||
| be assigned to the "web" middleware group. Make something great!
|
|
||||||
|
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Route::prefix('magic')->middleware(['auth'])->group(function () {
|
Route::prefix('magic')->middleware(['auth'])->group(function () {
|
||||||
Route::get('/servers', function () {
|
Route::get('/servers', [MagicController::class, 'servers']);
|
||||||
$id = session('currentTeam')->id;
|
Route::get('/destinations', [MagicController::class, 'destinations']);
|
||||||
return response()->json([
|
Route::get('/projects', [MagicController::class, 'projects']);
|
||||||
'servers' => Server::where('team_id', $id)->get()->sortBy('name')
|
Route::get('/environments', [MagicController::class, 'environments']);
|
||||||
]);
|
Route::get('/project/new', [MagicController::class, 'new_project']);
|
||||||
});
|
Route::get('/environment/new', [MagicController::class, 'new_environment']);
|
||||||
Route::get('/destinations', function () {
|
|
||||||
$server_id = request()->query('server_id');
|
|
||||||
return response()->json([
|
|
||||||
'destinations' => Server::destinations($server_id)->sortBy('name')
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
Route::get('/projects', function () {
|
|
||||||
$id = session('currentTeam')->id;
|
|
||||||
return response()->json([
|
|
||||||
'projects' => Project::where('team_id', $id)->get()->sortBy('name')
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
Route::get('/project/new', function () {
|
|
||||||
$id = session('currentTeam')->id;
|
|
||||||
$name = request()->query('name') ?? generate_random_name();
|
|
||||||
ray($id, $name);
|
|
||||||
$project = Project::create([
|
|
||||||
'name' => $name,
|
|
||||||
'team_id' => $id,
|
|
||||||
]);
|
|
||||||
return response()->json([
|
|
||||||
'new_project_id' => $project->id,
|
|
||||||
'new_project_uuid' => $project->uuid
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
Route::get('/environments', function () {
|
|
||||||
$id = session('currentTeam')->id;
|
|
||||||
$project_id = request()->query('project_id');
|
|
||||||
return response()->json([
|
|
||||||
'environments' => Project::where('team_id', $id)->where('id', $project_id)->first()->environments
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
Route::get('/environment/new', function () {
|
|
||||||
$id = session('currentTeam')->id;
|
|
||||||
$project_uuid = request()->query('project_uuid');
|
|
||||||
$name = request()->query('name') ?? generate_random_name();
|
|
||||||
ray($project_uuid, $name);
|
|
||||||
$project = Project::where('team_id', $id)->where('uuid', $project_uuid)->first();
|
|
||||||
$environment = $project->environments->where('name', $name)->first();
|
|
||||||
if (!$environment) {
|
|
||||||
$environment = Environment::create([
|
|
||||||
'name' => $name,
|
|
||||||
'project_id' => $project->id,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
return response()->json([
|
|
||||||
'new_environment_name' => $environment->name,
|
|
||||||
'project_id' => $project->id,
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
Route::middleware(['auth'])->group(function () {
|
Route::middleware(['auth'])->group(function () {
|
||||||
Route::get('/', function () {
|
Route::get('/projects', [ProjectController::class, 'all'])->name('projects');
|
||||||
$id = session('currentTeam')->id;
|
Route::get('/project/{project_uuid}', [ProjectController::class, 'show'])->name('project.show');
|
||||||
$projects = Project::where('team_id', $id)->get();
|
Route::get('/project/{project_uuid}/{environment_name}/new', [ProjectController::class, 'new'])->name('project.resources.new');
|
||||||
$servers = Server::where('team_id', $id)->get()->count();
|
Route::get('/project/{project_uuid}/{environment_name}', [ProjectController::class, 'resources'])->name('project.resources');
|
||||||
$resources = 0;
|
Route::get('/project/{project_uuid}/{environment_name}/application/{application_uuid}', [ApplicationController::class, 'configuration'])->name('project.application.configuration');
|
||||||
foreach ($projects as $project) {
|
|
||||||
$resources += $project->applications->count();
|
|
||||||
}
|
|
||||||
return view('dashboard', [
|
|
||||||
'servers' => $servers,
|
|
||||||
'projects' => $projects->count(),
|
|
||||||
'resources' => $resources,
|
|
||||||
]);
|
|
||||||
})->name('dashboard');
|
|
||||||
|
|
||||||
Route::get('/profile', function (Request $request) {
|
Route::get('/project/{project_uuid}/{environment_name}/application/{application_uuid}/deployment', [ApplicationController::class, 'deployments'])->name('project.application.deployments');
|
||||||
return view('profile', [
|
|
||||||
'request' => $request,
|
|
||||||
]);
|
|
||||||
})->name('profile');
|
|
||||||
|
|
||||||
Route::get('/profile/team', function () {
|
Route::get(
|
||||||
return view('team.show');
|
'/project/{project_uuid}/{environment_name}/application/{application_uuid}/deployment/{deployment_uuid}',
|
||||||
})->name('team.show');
|
[ApplicationController::class, 'deployment']
|
||||||
Route::get('/profile/team/notifications', function () {
|
)->name('project.application.deployment');
|
||||||
return view('team.notifications');
|
});
|
||||||
})->name('team.notifications');
|
|
||||||
|
|
||||||
Route::get('/settings', function () {
|
Route::middleware(['auth'])->group(function () {
|
||||||
$isRoot = auth()->user()->isPartOfRootTeam();
|
Route::get('/servers', [ServerController::class, 'all'])->name('server.all');
|
||||||
if ($isRoot) {
|
Route::get('/server/new', [ServerController::class, 'create'])->name('server.create');
|
||||||
$settings = InstanceSettings::get();
|
Route::get('/server/{server_uuid}', [ServerController::class, 'show'])->name('server.show');
|
||||||
return view('settings', [
|
Route::get('/server/{server_uuid}/proxy', [ServerController::class, 'proxy'])->name('server.proxy');
|
||||||
'settings' => $settings
|
Route::get('/server/{server_uuid}/private-key', fn () => view('server.private-key'))->name('server.private-key');
|
||||||
]);
|
});
|
||||||
} else {
|
|
||||||
return redirect()->route('dashboard');
|
|
||||||
}
|
|
||||||
})->name('settings');
|
|
||||||
|
|
||||||
Route::get('/update', function () {
|
|
||||||
return view('update');
|
|
||||||
})->name('update');
|
|
||||||
|
|
||||||
Route::get('/command-center', function () {
|
Route::middleware(['auth'])->group(function () {
|
||||||
$servers = Server::validated();
|
Route::get('/', [Controller::class, 'dashboard'])->name('dashboard');
|
||||||
return view('command-center', [
|
Route::get('/settings', [Controller::class, 'settings'])->name('settings');
|
||||||
'servers' => $servers,
|
Route::get('/profile', fn () => view('profile', ['request' => request()]))->name('profile');
|
||||||
]);
|
Route::get('/profile/team', fn () => view('team.show'))->name('team.show');
|
||||||
})->name('command-center');
|
Route::get('/profile/team/notifications', fn () => view('team.notifications'))->name('team.notifications');
|
||||||
|
Route::get('/command-center', fn () => view('command-center', ['servers' => Server::validated()->get()]))->name('command-center');
|
||||||
});
|
});
|
||||||
|
|
||||||
Route::middleware(['auth'])->group(function () {
|
Route::middleware(['auth'])->group(function () {
|
||||||
Route::get('/private-key/new', fn () => view('private-key.new'))->name('private-key.new');
|
Route::get('/private-key/new', fn () => view('private-key.new'))->name('private-key.new');
|
||||||
Route::get('/private-key/{private_key_uuid}', function () {
|
Route::get('/private-key/{private_key_uuid}', fn () => view('private-key.show', [
|
||||||
$private_key = PrivateKey::where('uuid', request()->private_key_uuid)->first();
|
'private_key' => PrivateKey::ownedByCurrentTeam()->whereUuid(request()->private_key_uuid)->firstOrFail()
|
||||||
return view('private-key.show', [
|
]))->name('private-key.show');
|
||||||
'private_key' => $private_key,
|
|
||||||
]);
|
|
||||||
})->name('private-key.show');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
Route::middleware(['auth'])->group(function () {
|
Route::middleware(['auth'])->group(function () {
|
||||||
Route::get('/source/new', fn () => view('source.new'))->name('source.new');
|
Route::get('/source/new', fn () => view('source.new'))->name('source.new');
|
||||||
Route::get('/source/github/{github_app_uuid}', function (Request $request) {
|
Route::get('/source/github/{github_app_uuid}', function (Request $request) {
|
||||||
@ -167,46 +86,10 @@
|
|||||||
]);
|
]);
|
||||||
})->name('source.github.show');
|
})->name('source.github.show');
|
||||||
});
|
});
|
||||||
Route::middleware(['auth'])->group(function () {
|
|
||||||
Route::get('/servers', fn () => view('servers', [
|
|
||||||
'servers' => Server::where('team_id', session('currentTeam')->id)->get(),
|
|
||||||
]))->name('servers');
|
|
||||||
|
|
||||||
Route::get('/server/new', fn () => view('server.new', [
|
|
||||||
'private_keys' => PrivateKey::where('team_id', session('currentTeam')->id)->get(),
|
|
||||||
]))->name('server.new');
|
|
||||||
|
|
||||||
Route::get('/server/{server_uuid}', function () {
|
|
||||||
$team_id = session('currentTeam')->id;
|
|
||||||
$server = Server::where('team_id', $team_id)->where('uuid', request()->server_uuid)->first();
|
|
||||||
if (!$server) {
|
|
||||||
return redirect()->route('dashboard');
|
|
||||||
}
|
|
||||||
return view('server.show', [
|
|
||||||
'server' => $server,
|
|
||||||
]);
|
|
||||||
})->name('server.show');
|
|
||||||
|
|
||||||
Route::get('/server/{server_uuid}/proxy', function () {
|
|
||||||
$team_id = session('currentTeam')->id;
|
|
||||||
$server = Server::where('team_id', $team_id)->where('uuid', request()->server_uuid)->first();
|
|
||||||
if (!$server) {
|
|
||||||
return redirect()->route('dashboard');
|
|
||||||
}
|
|
||||||
return view('server.proxy', [
|
|
||||||
'server' => $server,
|
|
||||||
]);
|
|
||||||
})->name('server.proxy');
|
|
||||||
|
|
||||||
|
|
||||||
Route::get('/server/{server_uuid}/private-key', function () {
|
|
||||||
return view('server.private-key');
|
|
||||||
})->name('server.private-key');
|
|
||||||
});
|
|
||||||
|
|
||||||
Route::middleware(['auth'])->group(function () {
|
Route::middleware(['auth'])->group(function () {
|
||||||
Route::get('/destination/new', function () {
|
Route::get('/destination/new', function () {
|
||||||
$servers = Server::validated();
|
$servers = Server::validated()->get();
|
||||||
$pre_selected_server_uuid = data_get(request()->query(), 'server');
|
$pre_selected_server_uuid = data_get(request()->query(), 'server');
|
||||||
if ($pre_selected_server_uuid) {
|
if ($pre_selected_server_uuid) {
|
||||||
$server = $servers->firstWhere('uuid', $pre_selected_server_uuid);
|
$server = $servers->firstWhere('uuid', $pre_selected_server_uuid);
|
||||||
@ -231,40 +114,3 @@
|
|||||||
]);
|
]);
|
||||||
})->name('destination.show');
|
})->name('destination.show');
|
||||||
});
|
});
|
||||||
|
|
||||||
Route::middleware(['auth'])->group(function () {
|
|
||||||
Route::get(
|
|
||||||
'/projects',
|
|
||||||
[ProjectController::class, 'all']
|
|
||||||
)->name('projects');
|
|
||||||
|
|
||||||
Route::get(
|
|
||||||
'/project/{project_uuid}',
|
|
||||||
[ProjectController::class, 'show']
|
|
||||||
)->name('project.show');
|
|
||||||
|
|
||||||
Route::get(
|
|
||||||
'/project/{project_uuid}/{environment_name}/new',
|
|
||||||
[ProjectController::class, 'new']
|
|
||||||
)->name('project.resources.new');
|
|
||||||
|
|
||||||
Route::get(
|
|
||||||
'/project/{project_uuid}/{environment_name}',
|
|
||||||
[ProjectController::class, 'resources']
|
|
||||||
)->name('project.resources');
|
|
||||||
|
|
||||||
Route::get(
|
|
||||||
'/project/{project_uuid}/{environment_name}/application/{application_uuid}',
|
|
||||||
[ApplicationController::class, 'configuration']
|
|
||||||
)->name('project.application.configuration');
|
|
||||||
|
|
||||||
Route::get(
|
|
||||||
'/project/{project_uuid}/{environment_name}/application/{application_uuid}/deployment',
|
|
||||||
[ApplicationController::class, 'deployments']
|
|
||||||
)->name('project.application.deployments');
|
|
||||||
|
|
||||||
Route::get(
|
|
||||||
'/project/{project_uuid}/{environment_name}/application/{application_uuid}/deployment/{deployment_uuid}',
|
|
||||||
[ApplicationController::class, 'deployment']
|
|
||||||
)->name('project.application.deployment');
|
|
||||||
});
|
|
||||||
|
Loading…
Reference in New Issue
Block a user