This commit is contained in:
Joao Patricio 2023-05-12 19:22:16 +01:00
commit ee084b0bd4
128 changed files with 3944 additions and 650 deletions

View File

@ -6,6 +6,8 @@
USERID=
GROUPID=
PROJECT_PATH_ON_HOST=/Users/your-username-here/code/coollabsio/coolify
SERVEO_URL=<for receiving webhooks locally https://serveo.net/>
MUX_ENABLED=false
############################################################################################################
APP_NAME=Coolify
@ -26,4 +28,5 @@ DB_DATABASE=coolify
DB_USERNAME=coolify
DB_PASSWORD=password
QUEUE_CONNECTION=database
QUEUE_CONNECTION=redis
REDIS_HOST=coolify-redis

View File

@ -15,4 +15,6 @@ DB_DATABASE=coolify
DB_USERNAME=coolify
DB_PASSWORD=
QUEUE_CONNECTION=database
QUEUE_CONNECTION=redis
REDIS_HOST=coolify-redis
REDIS_PASSWORD=

View File

@ -28,7 +28,7 @@ class SyncBunny extends Command
*/
public function handle()
{
$bunny_cdn = "https://coolify-cdn.b-cdn.net/";
$bunny_cdn = "https://coolify-cdn.b-cdn.net";
$bunny_cdn_path = "files";
$bunny_cdn_storage_name = "coolify-cdn";
@ -36,10 +36,13 @@ public function handle()
$compose_file = "docker-compose.yml";
$compose_file_prod = "docker-compose.prod.yml";
$install_script = "install.sh";
$upgrade_script = "upgrade.sh";
$docker_install_script = "install-docker.sh";
$production_env = ".env.production";
$versions = "versions.json";
PendingRequest::macro('storage', function ($file) {
$headers = [
'AccessKey' => env('BUNNY_STORAGE_API_KEY'),
@ -50,29 +53,36 @@ public function handle()
$file = fread($fileStream, filesize($file));
return PendingRequest::baseUrl('https://storage.bunnycdn.com')->withHeaders($headers)->withBody($file)->throw();
});
PendingRequest::macro('purge', function ($url) {
$headers = [
'AccessKey' => env('BUNNY_API_KEY'),
'Accept' => 'application/json',
];
return PendingRequest::withHeaders($headers)->post('https://api.bunny.net/purge', [
"urls" => [$url],
]);
});
try {
Http::pool(fn (Pool $pool) => [
$pool->storage(file: "$parent_dir/$compose_file")->put("/$bunny_cdn_storage_name/$bunny_cdn_path/$compose_file"),
$pool->storage(file: "$parent_dir/$compose_file_prod")->put("/$bunny_cdn_storage_name/$bunny_cdn_path/$compose_file_prod"),
$pool->storage(file: "$parent_dir/$production_env")->put("/$bunny_cdn_storage_name/$bunny_cdn_path/$production_env"),
$pool->storage(file: "$parent_dir/scripts/$upgrade_script")->put("/$bunny_cdn_storage_name/$bunny_cdn_path/$upgrade_script"),
$pool->storage(file: "$parent_dir/scripts/$install_script")->put("/$bunny_cdn_storage_name/$bunny_cdn_path/$install_script"),
$pool->storage(file: "$parent_dir/scripts/$docker_install_script")->put("/$bunny_cdn_storage_name/$bunny_cdn_path/$docker_install_script"),
$pool->storage(file: "$parent_dir/$versions")->put("/$bunny_cdn_storage_name/$versions"),
]);
$res = Http::withHeaders([
'AccessKey' => env('BUNNY_API_KEY'),
'Accept' => 'application/json',
])->get('https://api.bunny.net/purge', [
"url" => "$bunny_cdn/$bunny_cdn_path/$compose_file",
"url" => "$bunny_cdn/$bunny_cdn_path/$compose_file_prod",
"url" => "$bunny_cdn/$bunny_cdn_path/$upgrade_script",
"url" => "$bunny_cdn/$bunny_cdn_path/$production_env",
"url" => "$bunny_cdn/$bunny_cdn_path/$docker_install_script"
Http::pool(fn (Pool $pool) => [
$pool->purge(url: "$bunny_cdn/$bunny_cdn_path/$compose_file"),
$pool->purge(url: "$bunny_cdn/$bunny_cdn_path/$compose_file_prod"),
$pool->purge(url: "$bunny_cdn/$bunny_cdn_path/$production_env"),
$pool->purge(url: "$bunny_cdn/$bunny_cdn_path/$upgrade_script"),
$pool->purge(url: "$bunny_cdn/$bunny_cdn_path/$install_script"),
$pool->purge(url: "$bunny_cdn/$bunny_cdn_path/$docker_install_script"),
$pool->purge(url: "$bunny_cdn/$versions"),
]);
if ($res->ok()) {
echo "All files uploaded & purged...\n";
return;
}
echo "All files uploaded & purged...\n";
return;
throw new \Exception("Something went wrong.");
} catch (\Exception $e) {
echo $e->getMessage();

View File

@ -20,7 +20,7 @@ public function __construct(
public string $type,
public ?string $type_uuid = null,
public ?Model $model = null,
public string $status = ProcessStatus::HOLDING->value,
public string $status = ProcessStatus::QUEUED->value,
) {
}
}

View File

@ -4,7 +4,7 @@
enum ProcessStatus: string
{
case HOLDING = 'holding';
case QUEUED = 'queued';
case IN_PROGRESS = 'in_progress';
case FINISHED = 'finished';
case ERROR = 'error';

View File

@ -21,7 +21,7 @@ public function configuration()
if (!$application) {
return redirect()->route('dashboard');
}
return view('project.application.configuration', ['application' => $application]);
return view('project.application.configuration', ['application' => $application,]);
}
public function deployments()
{

View File

@ -19,7 +19,7 @@ public function environments()
return view('project.environments', ['project' => $project]);
}
public function resources_new()
public function new()
{
$project = session('currentTeam')->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first();
if (!$project) {
@ -29,7 +29,12 @@ public function resources_new()
if (!$environment) {
return redirect()->route('dashboard');
}
return view('project.new', ['project' => $project, 'environment' => $environment, 'type' => 'resource']);
$type = request()->query('type');
return view('project.new', [
'type' => $type
]);
}
public function resources()
{

View File

@ -2,6 +2,7 @@
namespace App\Http\Livewire;
use App\Enums\ProcessStatus;
use Livewire\Component;
use Spatie\Activitylog\Models\Activity;
@ -31,12 +32,24 @@ public function newMonitorActivity($activityId)
public function polling()
{
$this->hydrateActivity();
if (data_get($this->activity, 'properties.exitCode') !== null) {
$this->setStatus(ProcessStatus::IN_PROGRESS);
$exit_code = data_get($this->activity, 'properties.exitCode');
if ($exit_code !== null) {
if ($exit_code === 0) {
$this->setStatus(ProcessStatus::FINISHED);
} else {
$this->setStatus(ProcessStatus::ERROR);
}
$this->isPollingActive = false;
}
}
protected function setStatus($status)
{
$this->activity->properties = $this->activity->properties->merge([
'status' => $status,
]);
$this->activity->save();
}
public function render()
{
return view('livewire.activity-monitor');

View File

@ -2,8 +2,6 @@
namespace App\Http\Livewire;
use App\Models\Server;
use Illuminate\Support\Facades\Http;
use Livewire\Component;
class CheckUpdate extends Component
@ -16,7 +14,7 @@ class CheckUpdate extends Component
public function checkUpdate()
{
$this->latestVersion = getLatestVersionOfCoolify();
$this->currentVersion = config('coolify.version');
$this->currentVersion = config('version');
if ($this->latestVersion === 'latest') {
$this->updateAvailable = true;
return;

View File

@ -22,7 +22,7 @@ public function submit()
}
public function delete()
{
instantRemoteProcess(['docker network rm -f ' . $this->destination->network], $this->destination->server);
// instantRemoteProcess(['docker network rm -f ' . $this->destination->network], $this->destination->server);
$this->destination->delete();
return redirect()->route('dashboard');
}

View File

@ -14,7 +14,7 @@ class StandaloneDocker extends Component
public string $network;
public Collection $servers;
public int $server_id;
public int|null $server_id = null;
protected $rules = [
'name' => 'required|string',
@ -23,10 +23,14 @@ class StandaloneDocker extends Component
];
public function mount()
{
if (request()->query('server_id')) {
$this->server_id = request()->query('server_id');
} else {
$this->server_id = Server::first()->id;
if (!$this->server_id) {
if (request()->query('server_id')) {
$this->server_id = request()->query('server_id');
} else {
if ($this->servers->count() > 0) {
$this->server_id = $this->servers->first()->id;
}
}
}
$this->network = new Cuid2(7);
$this->name = generateRandomName();

View File

@ -35,7 +35,7 @@ public function changePrivateKey()
$this->private_key->save();
session('currentTeam')->privateKeys = PrivateKey::where('team_id', session('currentTeam')->id)->get();
} catch (\Exception $e) {
$this->addError('private_key_value', $e->getMessage());
return generalErrorHandler($e, $this);
}
}
}

View File

@ -3,15 +3,14 @@
namespace App\Http\Livewire\PrivateKey;
use App\Models\PrivateKey;
use Illuminate\Routing\Route as RoutingRoute;
use Illuminate\Support\Facades\Route;
use Livewire\Component;
class Create extends Component
{
public $private_key_value;
public $private_key_name;
public $private_key_description;
public string $name;
public string|null $description;
public string $value;
public string $currentRoute;
public function mount()
@ -20,14 +19,14 @@ public function mount()
}
public function createPrivateKey()
{
$this->private_key_value = trim($this->private_key_value);
if (!str_ends_with($this->private_key_value, "\n")) {
$this->private_key_value .= "\n";
$this->value = trim($this->value);
if (!str_ends_with($this->value, "\n")) {
$this->value .= "\n";
}
$new_private_key = PrivateKey::create([
'name' => $this->private_key_name,
'description' => $this->private_key_description,
'private_key' => $this->private_key_value,
'name' => $this->name,
'description' => $this->description,
'private_key' => $this->value,
'team_id' => session('currentTeam')->id
]);
session('currentTeam')->privateKeys = PrivateKey::where('team_id', session('currentTeam')->id)->get();

View File

@ -23,7 +23,7 @@ class Deploy extends Component
public function mount()
{
$this->parameters = Route::current()->parameters();
$this->parameters = getParameters();
$this->application = Application::where('id', $this->applicationId)->first();
$this->destination = $this->application->destination->getMorphClass()::where('id', $this->application->destination->id)->first();
}

View File

@ -23,7 +23,7 @@ class Add extends Component
];
public function mount()
{
$this->parameters = Route::current()->parameters();
$this->parameters = getParameters();
}
public function submit()
{

View File

@ -26,7 +26,7 @@ public function submit($data)
$this->application->refresh();
$this->emit('clearAddEnv');
} catch (\Exception $e) {
return generalErrorHandlerLivewire($e, $this);
return generalErrorHandler($e, $this);
}
}
}

View File

@ -20,7 +20,7 @@ class Show extends Component
];
public function mount()
{
$this->parameters = Route::current()->parameters();
$this->parameters = getParameters();
}
public function submit()
{

View File

@ -57,6 +57,7 @@ public function instantSave()
$this->application->settings->is_dual_cert = $this->is_dual_cert;
$this->application->settings->save();
$this->application->refresh();
$this->emit('saved', 'Application settings updated!');
}
public function mount()
{

View File

@ -19,4 +19,13 @@ public function mount()
{
$this->application = Application::where('id', $this->applicationId)->first();
}
public function submit()
{
$this->validate();
if (!$this->application->git_commit_sha) {
$this->application->git_commit_sha = 'HEAD';
}
$this->application->save();
$this->emit('saved', 'Application source updated!');
}
}

View File

@ -23,7 +23,7 @@ class Add extends Component
];
public function mount()
{
$this->parameters = Route::current()->parameters();
$this->parameters = getParameters();
}
public function submit()
{

View File

@ -28,7 +28,7 @@ public function submit($data)
$this->application->refresh();
$this->emit('clearAddStorage');
} catch (\Exception $e) {
return generalErrorHandlerLivewire($e, $this);
return generalErrorHandler($e, $this);
}
}
}

View File

@ -0,0 +1,25 @@
<?php
namespace App\Http\Livewire\Project;
use App\Models\Project;
use Livewire\Component;
class Delete extends Component
{
public int $project_id;
public int $resource_count = 0;
public function delete()
{
$this->validate([
'project_id' => 'required|int',
]);
$project = Project::findOrFail($this->project_id);
if ($project->applications->count() > 0) {
return $this->emit('error', 'Project has applications, please delete them first.');
}
$project->delete();
return redirect()->route('dashboard');
}
}

View File

@ -0,0 +1,142 @@
<?php
namespace App\Http\Livewire\Project\New;
use App\Models\Application;
use App\Models\GithubApp;
use App\Models\Project;
use App\Models\Server;
use App\Models\StandaloneDocker;
use App\Models\SwarmDocker;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
use Livewire\Component;
class GithubPrivateRepository extends Component
{
public $github_apps;
public GithubApp $github_app;
public $parameters;
public $query;
public $type;
public int $selected_repository_id;
public string $selected_repository_owner;
public string $selected_repository_repo;
public string $selected_branch_name = 'main';
public string $token;
protected int $page = 1;
public $repositories;
public int $total_repositories_count = 0;
public $branches;
public int $total_branches_count = 0;
protected function loadRepositoryByPage()
{
$response = Http::withToken($this->token)->get("{$this->github_app->api_url}/installation/repositories?per_page=100&page={$this->page}");
$json = $response->json();
if ($response->status() !== 200) {
return $this->emit('error', $json['message']);
}
if ($json['total_count'] === 0) {
return;
}
$this->total_repositories_count = $json['total_count'];
$this->repositories = $this->repositories->concat(collect($json['repositories']));
}
protected function loadBranchByPage()
{
Log::info('Loading page ' . $this->page);
$response = Http::withToken($this->token)->get("{$this->github_app->api_url}/repos/{$this->selected_repository_owner}/{$this->selected_repository_repo}/branches?per_page=100&page={$this->page}");
$json = $response->json();
if ($response->status() !== 200) {
return $this->emit('error', $json['message']);
}
$this->total_branches_count = count($json);
$this->branches = $this->branches->concat(collect($json));
}
public function loadRepositories($github_app_id)
{
$this->repositories = collect();
$this->page = 1;
$this->github_app = GithubApp::where('id', $github_app_id)->first();
$this->token = generate_github_installation_token($this->github_app);
$this->loadRepositoryByPage();
if ($this->repositories->count() < $this->total_repositories_count) {
while ($this->repositories->count() < $this->total_repositories_count) {
$this->page++;
$this->loadRepositoryByPage();
}
}
$this->selected_repository_id = $this->repositories[0]['id'];
}
public function loadBranches()
{
$this->selected_repository_owner = $this->repositories->where('id', $this->selected_repository_id)->first()['owner']['login'];
$this->selected_repository_repo = $this->repositories->where('id', $this->selected_repository_id)->first()['name'];
$this->branches = collect();
$this->page = 1;
$this->loadBranchByPage();
if ($this->total_branches_count === 100) {
while ($this->total_branches_count === 100) {
$this->page++;
$this->loadBranchByPage();
}
}
}
public function submit()
{
try {
$destination_uuid = $this->query['destination'];
$destination = StandaloneDocker::where('uuid', $destination_uuid)->first();
if (!$destination) {
$destination = SwarmDocker::where('uuid', $destination_uuid)->first();
}
if (!$destination) {
throw new \Exception('Destination not found. What?!');
}
$destination_class = $destination->getMorphClass();
$project = Project::where('uuid', $this->parameters['project_uuid'])->first();
$environment = $project->load(['environments'])->environments->where('name', $this->parameters['environment_name'])->first();
$application = Application::create([
'name' => generateRandomName() . "-{$this->selected_repository_owner}/{$this->selected_repository_repo}:{$this->selected_branch_name}",
'repository_project_id' => $this->selected_repository_id,
'git_repository' => "{$this->selected_repository_owner}/{$this->selected_repository_repo}",
'git_branch' => $this->selected_branch_name,
'build_pack' => 'nixpacks',
'ports_exposes' => '3000',
'environment_id' => $environment->id,
'destination_id' => $destination->id,
'destination_type' => $destination_class,
'source_id' => $this->github_app->id,
'source_type' => GithubApp::class,
]);
redirect()->route('project.application.configuration', [
'application_uuid' => $application->uuid,
'project_uuid' => $project->uuid,
'environment_name' => $environment->name
]);
} catch (\Exception $e) {
return generalErrorHandler($e, $this);
}
}
public function mount()
{
$this->parameters = getParameters();
$this->query = request()->query();
$this->repositories = $this->branches = collect();
$this->github_apps = GithubApp::private();
}
}

View File

@ -0,0 +1,102 @@
<?php
namespace App\Http\Livewire\Project\New;
use App\Models\Application;
use App\Models\PrivateKey;
use App\Models\Project;
use App\Models\StandaloneDocker;
use App\Models\SwarmDocker;
use Livewire\Component;
use Spatie\Url\Url;
class GithubPrivateRepositoryDeployKey extends Component
{
public $parameters;
public $query;
public $private_keys;
public int $private_key_id;
public string $repository_url;
public int $port = 3000;
public string $type;
public bool $is_static = false;
public null|string $publish_directory = null;
protected $rules = [
'repository_url' => 'required|url',
'port' => 'required|numeric',
'is_static' => 'required|boolean',
'publish_directory' => 'nullable|string',
];
public function mount()
{
if (config('app.env') === 'local') {
$this->repository_url = 'https://github.com/coollabsio/coolify-examples/tree/nodejs-fastify';
}
$this->parameters = getParameters();
$this->query = request()->query();
$this->private_keys = PrivateKey::where('team_id', session('currentTeam')->id)->get();
}
public function instantSave()
{
if ($this->is_static) {
$this->port = 80;
$this->publish_directory = '/dist';
} else {
$this->port = 3000;
$this->publish_directory = null;
}
}
public function setPrivateKey($private_key_id)
{
$this->private_key_id = $private_key_id;
}
public function submit()
{
try {
$this->validate();
$url = Url::fromString($this->repository_url);
$git_host = $url->getHost();
$git_repository = $url->getSegment(1) . '/' . $url->getSegment(2);
$git_branch = $url->getSegment(4) ?? 'main';
$destination_uuid = $this->query['destination'];
$destination = StandaloneDocker::where('uuid', $destination_uuid)->first();
if (!$destination) {
$destination = SwarmDocker::where('uuid', $destination_uuid)->first();
}
if (!$destination) {
throw new \Exception('Destination not found. What?!');
}
$destination_class = $destination->getMorphClass();
$project = Project::where('uuid', $this->parameters['project_uuid'])->first();
$environment = $project->load(['environments'])->environments->where('name', $this->parameters['environment_name'])->first();
$application_init = [
'name' => generateRandomName() . "-{$git_repository}:{$git_branch}",
'git_repository' => $git_repository,
'git_branch' => $git_branch,
'git_full_url' => "git@$git_host:$git_repository.git",
'build_pack' => 'nixpacks',
'ports_exposes' => $this->port,
'publish_directory' => $this->publish_directory,
'environment_id' => $environment->id,
'destination_id' => $destination->id,
'destination_type' => $destination_class,
'private_key_id' => $this->private_key_id,
];
$application = Application::create($application_init);
$application->settings->is_static = $this->is_static;
$application->settings->save();
return redirect()->route('project.application.configuration', [
'project_uuid' => $project->uuid,
'environment_name' => $environment->name,
'application_uuid' => $application->uuid,
]);
} catch (\Exception $e) {
return generalErrorHandler($e, $this);
}
}
}

View File

@ -8,22 +8,17 @@
use App\Models\Project;
use App\Models\StandaloneDocker;
use App\Models\SwarmDocker;
use Illuminate\Support\Facades\Route;
use Livewire\Component;
use Spatie\Url\Url;
class PublicGitRepository extends Component
{
public string $public_repository_url;
public int $port;
public string $repository_url;
public int $port = 3000;
public string $type;
public $parameters;
public $query;
public $servers;
public $standalone_docker;
public $swarm_docker;
public $chosenServer;
public $chosenDestination;
public $github_apps;
public $gitlab_apps;
@ -31,7 +26,7 @@ class PublicGitRepository extends Component
public null|string $publish_directory = null;
protected $rules = [
'public_repository_url' => 'required|url',
'repository_url' => 'required|url',
'port' => 'required|numeric',
'is_static' => 'required|boolean',
'publish_directory' => 'nullable|string',
@ -39,23 +34,11 @@ class PublicGitRepository extends Component
public function mount()
{
if (config('app.env') === 'local') {
$this->public_repository_url = 'https://github.com/coollabsio/coolify-examples/tree/nodejs-fastify';
$this->repository_url = 'https://github.com/coollabsio/coolify-examples/tree/nodejs-fastify';
$this->port = 3000;
}
$this->parameters = Route::current()->parameters();
$this->servers = session('currentTeam')->load(['servers'])->servers;
}
public function chooseServer($server)
{
$this->chosenServer = $server;
$this->standalone_docker = StandaloneDocker::where('server_id', $server['id'])->get();
$this->swarm_docker = SwarmDocker::where('server_id', $server['id'])->get();
}
public function setDestination($destination_uuid, $destination_type)
{
$class = "App\Models\\{$destination_type}";
$instance = new $class;
$this->chosenDestination = $instance::where('uuid', $destination_uuid)->first();
$this->parameters = getParameters();
$this->query = request()->query();
}
public function instantSave()
@ -67,53 +50,63 @@ public function instantSave()
$this->port = 3000;
$this->publish_directory = null;
}
$this->emit('saved', 'Application settings updated!');
}
public function submit()
{
$this->validate();
$url = Url::fromString($this->public_repository_url);
$git_host = $url->getHost();
$git_repository = $url->getSegment(1) . '/' . $url->getSegment(2);
$git_branch = $url->getSegment(4) ?? 'main';
try {
$this->validate();
if ($this->type === 'project') {
$project = Project::create([
'name' => generateRandomName(),
'team_id' => session('currentTeam')->id,
$url = Url::fromString($this->repository_url);
$git_host = $url->getHost();
$git_repository = $url->getSegment(1) . '/' . $url->getSegment(2);
$git_branch = $url->getSegment(4) ?? 'main';
$destination_uuid = $this->query['destination'];
$destination = StandaloneDocker::where('uuid', $destination_uuid)->first();
if (!$destination) {
$destination = SwarmDocker::where('uuid', $destination_uuid)->first();
}
if (!$destination) {
throw new \Exception('Destination not found. What?!');
}
$destination_class = $destination->getMorphClass();
$project = Project::where('uuid', $this->parameters['project_uuid'])->first();
$environment = $project->load(['environments'])->environments->where('name', $this->parameters['environment_name'])->first();
$application_init = [
'name' => generateRandomName() . "-{$git_repository}:{$git_branch}",
'git_repository' => $git_repository,
'git_branch' => $git_branch,
'build_pack' => 'nixpacks',
'ports_exposes' => $this->port,
'publish_directory' => $this->publish_directory,
'environment_id' => $environment->id,
'destination_id' => $destination->id,
'destination_type' => $destination_class,
];
if ($git_host == 'github.com') {
$application_init['source_id'] = GithubApp::where('name', 'Public GitHub')->first()->id;
$application_init['source_type'] = GithubApp::class;
} elseif ($git_host == 'gitlab.com') {
$application_init['source_id'] = GitlabApp::where('name', 'Public GitLab')->first()->id;
$application_init['source_type'] = GitlabApp::class;
} elseif ($git_host == 'bitbucket.org') {
}
$application = Application::create($application_init);
$application->settings->is_static = $this->is_static;
$application->settings->save();
return redirect()->route('project.application.configuration', [
'project_uuid' => $project->uuid,
'environment_name' => $environment->name,
'application_uuid' => $application->uuid,
]);
$environment = $project->environments->first();
} else {
$project = Project::where('uuid', $this->parameters['project_uuid'])->firstOrFail();
$environment = $project->environments->where('name', $this->parameters['environment_name'])->firstOrFail();
} catch (\Exception $e) {
return generalErrorHandler($e);
}
$application_init = [
'name' => generateRandomName(),
'git_repository' => $git_repository,
'git_branch' => $git_branch,
'build_pack' => 'nixpacks',
'ports_exposes' => $this->port,
'publish_directory' => $this->publish_directory,
'environment_id' => $environment->id,
'destination_id' => $this->chosenDestination->id,
'destination_type' => $this->chosenDestination->getMorphClass(),
];
if ($git_host == 'github.com') {
$application_init['source_id'] = GithubApp::where('name', 'Public GitHub')->first()->id;
$application_init['source_type'] = GithubApp::class;
} elseif ($git_host == 'gitlab.com') {
$application_init['source_id'] = GitlabApp::where('name', 'Public GitLab')->first()->id;
$application_init['source_type'] = GitlabApp::class;
} elseif ($git_host == 'bitbucket.org') {
}
$application = Application::create($application_init);
$application->settings->is_static = $this->is_static;
$application->settings->save();
return redirect()->route('project.application.configuration', [
'project_uuid' => $project->uuid,
'environment_name' => $environment->name,
'application_uuid' => $application->uuid,
]);
}
}

View File

@ -8,7 +8,7 @@
class RunCommand extends Component
{
public $command;
public string $command;
public $server;
public $servers = [];
@ -16,16 +16,20 @@ class RunCommand extends Component
'server' => 'required',
'command' => 'required',
];
public function mount()
public function mount($servers)
{
$this->servers = Server::where('team_id', session('currentTeam')->id)->get();
$this->server = $this->servers[0]->uuid;
$this->servers = $servers;
$this->server = $servers[0]->uuid;
}
public function runCommand()
{
$this->validate();
$activity = remoteProcess([$this->command], Server::where('uuid', $this->server)->first(), ActivityTypes::INLINE->value);
$this->emit('newMonitorActivity', $activity->id);
try {
$this->validate();
$activity = remoteProcess([$this->command], Server::where('uuid', $this->server)->first(), ActivityTypes::INLINE->value);
$this->emit('newMonitorActivity', $activity->id);
} catch (\Exception $e) {
return generalErrorHandler($e);
}
}
}

View File

@ -20,10 +20,11 @@ class Form extends Component
'server.ip' => 'required',
'server.user' => 'required',
'server.port' => 'required',
'server.settings.is_validated' => 'required'
];
public function mount()
{
$this->server = Server::find($this->server_id);
$this->server = Server::find($this->server_id)->load(['settings']);
}
public function installDocker()
{
@ -38,6 +39,11 @@ public function checkServer()
if (!$this->uptime) {
$this->uptime = 'Server not reachable.';
throw new \Exception('Server not reachable.');
} else {
if (!$this->server->settings->is_validated) {
$this->server->settings->is_validated = true;
$this->server->settings->save();
}
}
$this->dockerVersion = instantRemoteProcess(['docker version|head -2|grep -i version'], $this->server, false);
if (!$this->dockerVersion) {
@ -48,6 +54,7 @@ public function checkServer()
$this->dockerComposeVersion = 'Not installed.';
}
} catch (\Exception $e) {
return generalErrorHandler($e, $this);
}
}
public function delete()

View File

@ -9,7 +9,7 @@
class ByIp extends Component
{
public $private_keys;
public int $private_key_id;
public int|null $private_key_id = null;
public $new_private_key_name;
public $new_private_key_description;
public $new_private_key_value;
@ -20,30 +20,40 @@ class ByIp extends Component
public string $user = 'root';
public int $port = 22;
protected $rules = [
'name' => 'required',
'ip' => 'required',
'user' => 'required',
'port' => 'required|integer',
];
public function mount()
{
$this->name = generateRandomName();
$this->private_keys = PrivateKey::where('team_id', session('currentTeam')->id)->get();
}
public function setPrivateKey($private_key_id)
public function setPrivateKey(string $private_key_id)
{
$this->private_key_id = $private_key_id;
}
public function submit()
{
if (!$this->private_key_id) {
$this->addError('private_key_id', 'The private key field is required.');
return;
try {
if (!$this->private_key_id) {
return $this->emit('error', 'You must select a private key');
}
$this->validate();
$server = Server::create([
'name' => $this->name,
'description' => $this->description,
'ip' => $this->ip,
'user' => $this->user,
'port' => $this->port,
'team_id' => session('currentTeam')->id,
'private_key_id' => $this->private_key_id
]);
return redirect()->route('server.show', $server->uuid);
} catch (\Exception $e) {
return generalErrorHandler($e);
}
$server = Server::create([
'name' => $this->name,
'description' => $this->description,
'ip' => $this->ip,
'user' => $this->user,
'port' => $this->port,
'team_id' => session('currentTeam')->id,
'private_key_id' => $this->private_key_id
]);
return redirect()->route('server.show', $server->uuid);
}
}

View File

@ -20,7 +20,7 @@ public function setPrivateKey($private_key_id)
}
public function mount()
{
$this->parameters = Route::current()->parameters();
$this->parameters = getParameters();
$this->private_keys = ModelsPrivateKey::where('team_id', session('currentTeam')->id)->get();
}
}

View File

@ -33,6 +33,7 @@ public function instantSave()
$this->settings->is_registration_enabled = $this->is_registration_enabled;
$this->settings->is_https_forced = $this->is_https_forced;
$this->settings->save();
$this->emit('saved', 'Settings updated!');
}
public function submit()
{

View File

@ -0,0 +1,49 @@
<?php
namespace App\Http\Livewire\Source;
use App\Models\GithubApp;
use Livewire\Component;
class Create extends Component
{
public string $name;
public string|null $organization = null;
public string $api_url = 'https://api.github.com';
public string $html_url = 'https://github.com';
public string $custom_user = 'git';
public int $custom_port = 22;
public bool $is_system_wide = false;
public function mount()
{
$this->name = generateRandomName();
}
public function createGitHubApp()
{
try {
$this->validate([
"name" => 'required|string',
"organization" => 'nullable|string',
"api_url" => 'required|string',
"html_url" => 'required|string',
"custom_user" => 'required|string',
"custom_port" => 'required|int',
"is_system_wide" => 'required|bool',
]);
$github_app = GithubApp::create([
'name' => $this->name,
'organization' => $this->organization,
'api_url' => $this->api_url,
'html_url' => $this->html_url,
'custom_user' => $this->custom_user,
'custom_port' => $this->custom_port,
'is_system_wide' => $this->is_system_wide,
'team_id' => session('currentTeam')->id,
]);
redirect()->route('source.github.show', ['github_app_uuid' => $github_app->uuid]);
} catch (\Exception $e) {
return generalErrorHandler($e, $this);
}
}
}

View File

@ -0,0 +1,67 @@
<?php
namespace App\Http\Livewire\Source\Github;
use App\Models\GithubApp;
use App\Models\InstanceSettings;
use Livewire\Component;
class Change extends Component
{
public string $host;
public $parameters;
public GithubApp $github_app;
public bool $is_system_wide;
protected $rules = [
'github_app.name' => 'required|string',
'github_app.organization' => 'nullable|string',
'github_app.api_url' => 'required|string',
'github_app.html_url' => 'required|string',
'github_app.custom_user' => 'required|string',
'github_app.custom_port' => 'required|int',
'github_app.app_id' => 'required|int',
'github_app.installation_id' => 'required|int',
'github_app.client_id' => 'required|string',
'github_app.client_secret' => 'required|string',
'github_app.webhook_secret' => 'required|string',
'github_app.is_system_wide' => 'required|bool',
];
public function submit()
{
try {
$this->validate();
$this->github_app->save();
} catch (\Exception $e) {
return generalErrorHandler($e, $this);
}
}
public function instantSave()
{
try {
$this->github_app->is_system_wide = $this->is_system_wide;
$this->github_app->save();
$this->emit('saved', 'GitHub settings updated!');
} catch (\Exception $e) {
return generalErrorHandler($e, $this);
}
}
public function mount()
{
$settings = InstanceSettings::first();
if ($settings->fqdn) {
$this->host = $settings->fqdn;
}
$this->parameters = getParameters();
$this->is_system_wide = $this->github_app->is_system_wide;
}
public function delete()
{
try {
$this->github_app->delete();
redirect()->route('dashboard');
} catch (\Exception $e) {
return generalErrorHandler($e, $this);
}
}
}

View File

@ -7,20 +7,13 @@
use App\Enums\ActivityTypes;
use App\Models\Application;
use App\Models\InstanceSettings;
use DateTimeImmutable;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Storage;
use Lcobucci\JWT\Encoding\ChainedFormatter;
use Lcobucci\JWT\Encoding\JoseEncoder;
use Lcobucci\JWT\Signer\Key\InMemory;
use Lcobucci\JWT\Signer\Rsa\Sha256;
use Lcobucci\JWT\Token\Builder;
use Spatie\Activitylog\Models\Activity;
use Symfony\Component\Yaml\Yaml;
use Illuminate\Support\Str;
@ -107,7 +100,10 @@ public function handle(): void
{
try {
$coolify_instance_settings = InstanceSettings::find(0);
$this->source = $this->application->source->getMorphClass()::where('id', $this->application->source->id)->first();
$deployment_type = $this->application->deploymentType();
if ($this->application->deploymentType() === 'source') {
$this->source = $this->application->source->getMorphClass()::where('id', $this->application->source->id)->first();
}
// Get Wildcard Domain
$project_wildcard_domain = data_get($this->application, 'environment.project.settings.wildcard_domain');
@ -372,34 +368,11 @@ private function generate_healthcheck_commands()
return implode(' ', $generated_healthchecks_commands);
}
private function generate_jwt_token_for_github()
{
$signingKey = InMemory::plainText($this->source->privateKey->private_key);
$algorithm = new Sha256();
$tokenBuilder = (new Builder(new JoseEncoder(), ChainedFormatter::default()));
$now = new DateTimeImmutable();
$now = $now->setTime($now->format('H'), $now->format('i'));
$issuedToken = $tokenBuilder
->issuedBy($this->source->app_id)
->issuedAt($now)
->expiresAt($now->modify('+10 minutes'))
->getToken($algorithm, $signingKey)
->toString();
$token = Http::withHeaders([
'Authorization' => "Bearer $issuedToken",
'Accept' => 'application/vnd.github.machine-man-preview+json'
])->post("{$this->source->api_url}/app/installations/{$this->source->installation_id}/access_tokens");
if ($token->failed()) {
throw new \Exception("Failed to get access token for $this->application->name from " . $this->source->name . " with error: " . $token->json()['message']);
}
return $token->json()['token'];
}
private function set_labels_for_applications()
{
$labels = [];
$labels[] = 'coolify.managed=true';
$labels[] = 'coolify.version=' . config('coolify.version');
$labels[] = 'coolify.version=' . config('version');
$labels[] = 'coolify.applicationId=' . $this->application->id;
$labels[] = 'coolify.type=application';
$labels[] = 'coolify.name=' . $this->application->name;
@ -453,8 +426,8 @@ private function executeNow(
}
private function setGitImportSettings($git_clone_command)
{
if ($this->application->git_commit_sha) {
$git_clone_command = "{$git_clone_command} && cd {$this->workdir} && git checkout {$this->application->git_commit_sha}";
if ($this->application->git_commit_sha !== 'HEAD') {
$git_clone_command = "{$git_clone_command} && cd {$this->workdir} && git -c advice.detachedHead=false checkout {$this->application->git_commit_sha} >/dev/null 2>&1";
}
if ($this->application->settings->is_git_submodules_allowed) {
$git_clone_command = "{$git_clone_command} && cd {$this->workdir} && git submodule update --init --recursive";
@ -466,41 +439,40 @@ private function setGitImportSettings($git_clone_command)
}
private function gitImport()
{
$source_html_url = data_get($this->application, 'source.html_url');
$url = parse_url(filter_var($source_html_url, FILTER_SANITIZE_URL));
$source_html_url_host = $url['host'];
$source_html_url_scheme = $url['scheme'];
$git_clone_command = "git clone -q -b {$this->application->git_branch}";
if ($this->application->source->getMorphClass() == 'App\Models\GithubApp') {
if ($this->source->is_public) {
$git_clone_command = "{$git_clone_command} {$this->source->html_url}/{$this->application->git_repository} {$this->workdir}";
$git_clone_command = $this->setGitImportSettings($git_clone_command);
return [
$this->execute_in_builder($git_clone_command)
];
} else {
if (!$this->application->source->app_id) {
$private_key = base64_encode($this->application->source->privateKey->private_key);
if ($this->application->deploymentType() === 'source') {
$source_html_url = data_get($this->application, 'source.html_url');
$url = parse_url(filter_var($source_html_url, FILTER_SANITIZE_URL));
$source_html_url_host = $url['host'];
$source_html_url_scheme = $url['scheme'];
$git_clone_command = "GIT_SSH_COMMAND=\"ssh -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa\" {$git_clone_command} git@$source_html_url_host:{$this->application->git_repository}.git {$this->workdir}";
if ($this->source->getMorphClass() == 'App\Models\GithubApp') {
if ($this->source->is_public) {
$git_clone_command = "{$git_clone_command} {$this->source->html_url}/{$this->application->git_repository} {$this->workdir}";
$git_clone_command = $this->setGitImportSettings($git_clone_command);
return [
$this->execute_in_builder("mkdir -p /root/.ssh"),
$this->execute_in_builder("echo '{$private_key}' | base64 -d > /root/.ssh/id_rsa"),
$this->execute_in_builder("chmod 600 /root/.ssh/id_rsa"),
$this->execute_in_builder($git_clone_command)
];
} else {
$github_access_token = $this->generate_jwt_token_for_github();
$github_access_token = generate_github_installation_token($this->source);
return [
$this->execute_in_builder("git clone -q -b {$this->application->git_branch} $source_html_url_scheme://x-access-token:$github_access_token@$source_html_url_host/{$this->application->git_repository}.git {$this->workdir}")
];
}
}
}
if ($this->application->deploymentType() === 'deploy_key') {
$private_key = base64_encode($this->application->private_key->private_key);
$git_clone_command = "GIT_SSH_COMMAND=\"ssh -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa\" {$git_clone_command} {$this->application->git_full_url} {$this->workdir}";
$git_clone_command = $this->setGitImportSettings($git_clone_command);
return [
$this->execute_in_builder("mkdir -p /root/.ssh"),
$this->execute_in_builder("echo '{$private_key}' | base64 -d > /root/.ssh/id_rsa"),
$this->execute_in_builder("chmod 600 /root/.ssh/id_rsa"),
$this->execute_in_builder($git_clone_command)
];
}
}
private function nixpacks_build_cmd()
{

View File

@ -24,9 +24,11 @@ protected static function booted()
protected $fillable = [
'name',
'project_id',
'description',
'git_repository',
'git_branch',
'git_full_url',
'build_pack',
'environment_id',
'destination_id',
@ -36,6 +38,7 @@ protected static function booted()
'ports_mappings',
'ports_exposes',
'publish_directory',
'private_key_id'
];
public function publishDirectory(): Attribute
{
@ -43,6 +46,28 @@ public function publishDirectory(): Attribute
set: fn ($value) => $value ? '/' . ltrim($value, '/') : null,
);
}
public function gitBranchLocation(): Attribute
{
return Attribute::make(
get: function () {
if (!is_null($this->source?->html_url) && !is_null($this->git_repository) && !is_null($this->git_branch)) {
return "{$this->source->html_url}/{$this->git_repository}/tree/{$this->git_branch}";
}
}
);
}
public function gitCommits(): Attribute
{
return Attribute::make(
get: function () {
if (!is_null($this->source?->html_url) && !is_null($this->git_repository) && !is_null($this->git_branch)) {
return "{$this->source->html_url}/{$this->git_repository}/commits/{$this->git_branch}";
}
}
);
}
public function baseDirectory(): Attribute
{
return Attribute::make(
@ -90,6 +115,10 @@ public function nixpacks_environment_variables(): HasMany
{
return $this->hasMany(EnvironmentVariable::class)->where('key', 'like', 'NIXPACKS_%');
}
public function private_key()
{
return $this->belongsTo(PrivateKey::class);
}
public function environment()
{
return $this->belongsTo(Environment::class);
@ -119,4 +148,21 @@ public function get_deployment(string $deployment_uuid)
{
return Activity::where('subject_id', $this->id)->where('properties->type_uuid', '=', $deployment_uuid)->first();
}
public function isDeployable(): bool
{
if ($this->settings->is_auto_deploy) {
return true;
}
return false;
}
public function deploymentType()
{
if (data_get($this, 'source')) {
return 'source';
}
if (data_get($this, 'private_key_id')) {
return 'deploy_key';
}
throw new \Exception('No deployment type found');
}
}

View File

@ -12,7 +12,10 @@ protected static function boot()
parent::boot();
static::creating(function (Model $model) {
$model->uuid = (string) new Cuid2(7);
// Generate a UUID if one isn't set
if (!$model->uuid) {
$model->uuid = (string) new Cuid2(7);
}
});
}
}

View File

@ -8,7 +8,6 @@
class EnvironmentVariable extends Model
{
protected $fillable = ['key', 'value', 'is_build_time', 'application_id'];
protected $casts = [
"key" => 'string',

View File

@ -4,9 +4,19 @@
class GithubApp extends BaseModel
{
protected $fillable = ['name', 'uuid', 'organization', 'api_url', 'html_url', 'custom_user', 'custom_port', 'team_id'];
protected $casts = [
'is_public' => 'boolean',
];
protected static function booted(): void
{
static::deleting(function (GithubApp $github_app) {
$applications_count = Application::where('source_id', $github_app->id)->count();
if ($applications_count > 0) {
throw new \Exception('You cannot delete this GitHub App because it is in use by ' . $applications_count . ' application(s). Delete them first.');
}
});
}
public function applications()
{
return $this->morphMany(Application::class, 'source');
@ -15,4 +25,12 @@ public function privateKey()
{
return $this->belongsTo(PrivateKey::class);
}
static public function public()
{
return GithubApp::where('team_id', session('currentTeam')->id)->where('is_public', true)->get();
}
static public function private()
{
return GithubApp::where('team_id', session('currentTeam')->id)->where('is_public', false)->get();
}
}

View File

@ -28,6 +28,7 @@ protected static function booted()
'extra_attributes' => SchemalessAttributes::class,
];
public function standaloneDockers()
{
return $this->hasMany(StandaloneDocker::class);
@ -52,4 +53,27 @@ public function settings()
{
return $this->hasOne(ServerSetting::class);
}
static public function validated()
{
return Server::where('team_id', session('currentTeam')->id)->whereRelation('settings', 'is_validated', true)->get();
}
static public function destinations(string|null $server_uuid = null)
{
if ($server_uuid) {
$server = Server::where('team_id', session('currentTeam')->id)->where('uuid', $server_uuid)->firstOrFail();
$standaloneDocker = collect($server->standaloneDockers->all());
$swarmDocker = collect($server->swarmDockers->all());
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);
}
}
}

View File

@ -74,4 +74,10 @@ public function otherTeams()
return $team->id != $team_id;
});
}
public function resources()
{
$team_id = data_get(session('currentTeam'), 'id');
$data = Application::where('team_id', $team_id)->get();
return $data;
}
}

View File

@ -16,7 +16,7 @@ class AppServiceProvider extends ServiceProvider
*/
public function register(): void
{
// if (config('app.env') === 'production' && Str::contains(config('coolify.version'), ['nightly'])) {
// if (config('app.env') === 'production' && Str::contains(config('version'), ['nightly'])) {
// Process::run('php artisan migrate:fresh --force --seed --seeder=ProductionSeeder');
// }
}

View File

@ -0,0 +1,41 @@
<?php
namespace App\Providers;
use App\Models\User;
use Illuminate\Support\Facades\Gate;
use Laravel\Horizon\Horizon;
use Laravel\Horizon\HorizonApplicationServiceProvider;
class HorizonServiceProvider extends HorizonApplicationServiceProvider
{
/**
* Bootstrap any application services.
*/
public function boot(): void
{
parent::boot();
// Horizon::routeSmsNotificationsTo('15556667777');
// Horizon::routeMailNotificationsTo('example@example.com');
// Horizon::routeSlackNotificationsTo('slack-webhook-url', '#channel');
Horizon::night();
}
/**
* Register the Horizon gate.
*
* This gate determines who can access Horizon in non-local environments.
*/
protected function gate(): void
{
Gate::define('viewHorizon', function ($user) {
$root_user = User::find(0);
return in_array($user->email, [
$root_user->email
]);
});
}
}

View File

@ -31,6 +31,9 @@ public function boot(): void
->prefix('api')
->group(base_path('routes/api.php'));
Route::prefix('webhooks')
->group(base_path('routes/webhooks.php'));
Route::middleware('web')
->group(base_path('routes/web.php'));
});

View File

@ -1,3 +1,3 @@
<?php
$config = include 'config/coolify.php';
echo $config['version'];
$version = include 'config/version.php';
echo $version;

View File

@ -2,6 +2,7 @@
use App\Actions\CoolifyTask\PrepareCoolifyTask;
use App\Data\CoolifyTaskArgs;
use App\Models\GithubApp;
use App\Models\Server;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\QueryException;
@ -9,20 +10,38 @@
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Process;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\Facades\Storage;
use Spatie\Activitylog\Contracts\Activity;
use Illuminate\Support\Str;
use Visus\Cuid2\Cuid2;
if (!function_exists('generalErrorHandlerLivewire')) {
function generalErrorHandlerLivewire(\Throwable $e, $that)
if (!function_exists('generalErrorHandler')) {
function generalErrorHandler(\Throwable $e, $that = null, $isJson = false)
{
if ($e instanceof QueryException) {
if ($e->errorInfo[0] === '23505') {
$that->emit('error', 'Duplicate entry found.');
try {
if ($e instanceof QueryException) {
if ($e->errorInfo[0] === '23505') {
throw new \Exception('Duplicate entry found.', '23505');
} else if (count($e->errorInfo) === 4) {
throw new \Exception($e->errorInfo[3]);
} else {
throw new \Exception($e->errorInfo[2]);
}
} else {
$that->emit('error', $e->errorInfo[3]);
throw new \Exception($e->getMessage());
}
} catch (\Throwable $error) {
if ($that) {
$that->emit('error', $error);
} elseif ($isJson) {
return response()->json([
'code' => $error->getCode(),
'error' => $error->getMessage(),
]);
} else {
dump($error);
}
} else {
$that->emit('error', $e);
}
}
}
@ -158,7 +177,7 @@ function instantRemoteProcess(array $command, Server $server, $throwError = true
if (!function_exists('getLatestVersionOfCoolify')) {
function getLatestVersionOfCoolify()
{
$response = Http::get('https://get.coollabs.io/versions.json');
$response = Http::get('https://coolify-cdn.b-cdn.net/versions.json');
$versions = $response->json();
return data_get($versions, 'coolify.v4.version');
}
@ -166,11 +185,62 @@ function getLatestVersionOfCoolify()
if (!function_exists('generateRandomName')) {
function generateRandomName()
{
$generator = new \Nubs\RandomNameGenerator\All(
[
new \Nubs\RandomNameGenerator\Alliteration()
]
);
return $generator->getName();
$generator = \Nubs\RandomNameGenerator\All::create();
$cuid = new Cuid2(7);
return Str::kebab("{$generator->getName()}-{$cuid}");
}
}
use Lcobucci\JWT\Encoding\ChainedFormatter;
use Lcobucci\JWT\Encoding\JoseEncoder;
use Lcobucci\JWT\Signer\Key\InMemory;
use Lcobucci\JWT\Signer\Rsa\Sha256;
use Lcobucci\JWT\Token\Builder;
if (!function_exists('generate_github_installation_token')) {
function generate_github_installation_token(GithubApp $source)
{
$signingKey = InMemory::plainText($source->privateKey->private_key);
$algorithm = new Sha256();
$tokenBuilder = (new Builder(new JoseEncoder(), ChainedFormatter::default()));
$now = new DateTimeImmutable();
$now = $now->setTime($now->format('H'), $now->format('i'));
$issuedToken = $tokenBuilder
->issuedBy($source->app_id)
->issuedAt($now)
->expiresAt($now->modify('+10 minutes'))
->getToken($algorithm, $signingKey)
->toString();
$token = Http::withHeaders([
'Authorization' => "Bearer $issuedToken",
'Accept' => 'application/vnd.github.machine-man-preview+json'
])->post("{$source->api_url}/app/installations/{$source->installation_id}/access_tokens");
if ($token->failed()) {
throw new \Exception("Failed to get access token for " . $source->name . " with error: " . $token->json()['message']);
}
return $token->json()['token'];
}
}
if (!function_exists('generate_github_jwt_token')) {
function generate_github_jwt_token(GithubApp $source)
{
$signingKey = InMemory::plainText($source->privateKey->private_key);
$algorithm = new Sha256();
$tokenBuilder = (new Builder(new JoseEncoder(), ChainedFormatter::default()));
$now = new DateTimeImmutable();
$now = $now->setTime($now->format('H'), $now->format('i'));
$issuedToken = $tokenBuilder
->issuedBy($source->app_id)
->issuedAt($now->modify('-1 minute'))
->expiresAt($now->modify('+10 minutes'))
->getToken($algorithm, $signingKey)
->toString();
return $issuedToken;
}
}
if (!function_exists('getParameters')) {
function getParameters()
{
return Route::current()->parameters();
}
}

View File

@ -10,16 +10,18 @@
"guzzlehttp/guzzle": "^7.5.0",
"laravel/fortify": "^v1.16.0",
"laravel/framework": "^v10.7.1",
"laravel/horizon": "^5.15",
"laravel/sanctum": "^v3.2.1",
"laravel/tinker": "^v2.8.1",
"laravel/ui": "^4.2",
"lcobucci/jwt": "^5.0.0",
"livewire/livewire": "^v2.12.3",
"nubs/random-name-generator": "^2.2",
"spatie/laravel-activitylog": "^4.7.3",
"spatie/laravel-data": "^3.4.3",
"spatie/laravel-ray": "^1.32.4",
"spatie/url": "^2.2",
"spatie/laravel-schemaless-attributes": "^2.4",
"spatie/url": "^2.2",
"symfony/yaml": "^6.2",
"visus/cuid2": "^2.0.0"
},

320
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "76cbf08c8a60e542b25b1d96f9f4e265",
"content-hash": "dab9ddb648c7ca0d8b7e750090bb8e10",
"packages": [
{
"name": "bacon/bacon-qr-code",
@ -1422,16 +1422,16 @@
},
{
"name": "laravel/fortify",
"version": "v1.17.1",
"version": "v1.17.2",
"source": {
"type": "git",
"url": "https://github.com/laravel/fortify.git",
"reference": "76908639d6c58a4996ce8bbacea9ec7f610b2ec6"
"reference": "fc4b9b00b0d657dd035751b286f412976596ba32"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/fortify/zipball/76908639d6c58a4996ce8bbacea9ec7f610b2ec6",
"reference": "76908639d6c58a4996ce8bbacea9ec7f610b2ec6",
"url": "https://api.github.com/repos/laravel/fortify/zipball/fc4b9b00b0d657dd035751b286f412976596ba32",
"reference": "fc4b9b00b0d657dd035751b286f412976596ba32",
"shasum": ""
},
"require": {
@ -1482,20 +1482,20 @@
"issues": "https://github.com/laravel/fortify/issues",
"source": "https://github.com/laravel/fortify"
},
"time": "2023-04-19T15:48:59+00:00"
"time": "2023-04-26T13:35:07+00:00"
},
{
"name": "laravel/framework",
"version": "v10.9.0",
"version": "v10.10.0",
"source": {
"type": "git",
"url": "https://github.com/laravel/framework.git",
"reference": "35078125f61ef0b125edf524de934f108d4b47fd"
"reference": "0da22a8d179f79b49d4e71f4822f759651f35012"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/framework/zipball/35078125f61ef0b125edf524de934f108d4b47fd",
"reference": "35078125f61ef0b125edf524de934f108d4b47fd",
"url": "https://api.github.com/repos/laravel/framework/zipball/0da22a8d179f79b49d4e71f4822f759651f35012",
"reference": "0da22a8d179f79b49d4e71f4822f759651f35012",
"shasum": ""
},
"require": {
@ -1682,7 +1682,85 @@
"issues": "https://github.com/laravel/framework/issues",
"source": "https://github.com/laravel/framework"
},
"time": "2023-04-25T13:47:18+00:00"
"time": "2023-05-09T13:08:05+00:00"
},
{
"name": "laravel/horizon",
"version": "v5.15.1",
"source": {
"type": "git",
"url": "https://github.com/laravel/horizon.git",
"reference": "952c2a0caa79b3fdc513e8db668029004f3a0f35"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/horizon/zipball/952c2a0caa79b3fdc513e8db668029004f3a0f35",
"reference": "952c2a0caa79b3fdc513e8db668029004f3a0f35",
"shasum": ""
},
"require": {
"ext-json": "*",
"ext-pcntl": "*",
"ext-posix": "*",
"illuminate/contracts": "^8.17|^9.0|^10.0",
"illuminate/queue": "^8.17|^9.0|^10.0",
"illuminate/support": "^8.17|^9.0|^10.0",
"nesbot/carbon": "^2.17",
"php": "^7.3|^8.0",
"ramsey/uuid": "^4.0",
"symfony/error-handler": "^5.0|^6.0",
"symfony/process": "^5.0|^6.0"
},
"require-dev": {
"mockery/mockery": "^1.0",
"orchestra/testbench": "^6.0|^7.0|^8.0",
"phpstan/phpstan": "^1.10",
"phpunit/phpunit": "^9.0",
"predis/predis": "^1.1|^2.0"
},
"suggest": {
"ext-redis": "Required to use the Redis PHP driver.",
"predis/predis": "Required when not using the Redis PHP driver (^1.1|^2.0)."
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "5.x-dev"
},
"laravel": {
"providers": [
"Laravel\\Horizon\\HorizonServiceProvider"
],
"aliases": {
"Horizon": "Laravel\\Horizon\\Horizon"
}
}
},
"autoload": {
"psr-4": {
"Laravel\\Horizon\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Taylor Otwell",
"email": "taylor@laravel.com"
}
],
"description": "Dashboard and code-driven configuration for Laravel queues.",
"keywords": [
"laravel",
"queue"
],
"support": {
"issues": "https://github.com/laravel/horizon/issues",
"source": "https://github.com/laravel/horizon/tree/v5.15.1"
},
"time": "2023-04-26T16:00:44+00:00"
},
{
"name": "laravel/sanctum",
@ -1878,6 +1956,68 @@
},
"time": "2023-02-15T16:40:09+00:00"
},
{
"name": "laravel/ui",
"version": "v4.2.1",
"source": {
"type": "git",
"url": "https://github.com/laravel/ui.git",
"reference": "05ff7ac1eb55e2dfd10edcfb18c953684d693907"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/ui/zipball/05ff7ac1eb55e2dfd10edcfb18c953684d693907",
"reference": "05ff7ac1eb55e2dfd10edcfb18c953684d693907",
"shasum": ""
},
"require": {
"illuminate/console": "^9.21|^10.0",
"illuminate/filesystem": "^9.21|^10.0",
"illuminate/support": "^9.21|^10.0",
"illuminate/validation": "^9.21|^10.0",
"php": "^8.0"
},
"require-dev": {
"orchestra/testbench": "^7.0|^8.0",
"phpunit/phpunit": "^9.3"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "4.x-dev"
},
"laravel": {
"providers": [
"Laravel\\Ui\\UiServiceProvider"
]
}
},
"autoload": {
"psr-4": {
"Laravel\\Ui\\": "src/",
"Illuminate\\Foundation\\Auth\\": "auth-backend/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Taylor Otwell",
"email": "taylor@laravel.com"
}
],
"description": "Laravel UI utilities and presets.",
"keywords": [
"laravel",
"ui"
],
"support": {
"source": "https://github.com/laravel/ui/tree/v4.2.1"
},
"time": "2023-02-17T09:17:24+00:00"
},
{
"name": "lcobucci/jwt",
"version": "5.0.0",
@ -2143,19 +2283,20 @@
},
{
"name": "league/flysystem",
"version": "3.14.0",
"version": "3.15.1",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/flysystem.git",
"reference": "e2a279d7f47d9098e479e8b21f7fb8b8de230158"
"reference": "a141d430414fcb8bf797a18716b09f759a385bed"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/thephpleague/flysystem/zipball/e2a279d7f47d9098e479e8b21f7fb8b8de230158",
"reference": "e2a279d7f47d9098e479e8b21f7fb8b8de230158",
"url": "https://api.github.com/repos/thephpleague/flysystem/zipball/a141d430414fcb8bf797a18716b09f759a385bed",
"reference": "a141d430414fcb8bf797a18716b09f759a385bed",
"shasum": ""
},
"require": {
"league/flysystem-local": "^3.0.0",
"league/mime-type-detection": "^1.0.0",
"php": "^8.0.2"
},
@ -2214,7 +2355,7 @@
],
"support": {
"issues": "https://github.com/thephpleague/flysystem/issues",
"source": "https://github.com/thephpleague/flysystem/tree/3.14.0"
"source": "https://github.com/thephpleague/flysystem/tree/3.15.1"
},
"funding": [
{
@ -2226,7 +2367,67 @@
"type": "github"
}
],
"time": "2023-04-11T18:11:47+00:00"
"time": "2023-05-04T09:04:26+00:00"
},
{
"name": "league/flysystem-local",
"version": "3.15.0",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/flysystem-local.git",
"reference": "543f64c397fefdf9cfeac443ffb6beff602796b3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/thephpleague/flysystem-local/zipball/543f64c397fefdf9cfeac443ffb6beff602796b3",
"reference": "543f64c397fefdf9cfeac443ffb6beff602796b3",
"shasum": ""
},
"require": {
"ext-fileinfo": "*",
"league/flysystem": "^3.0.0",
"league/mime-type-detection": "^1.0.0",
"php": "^8.0.2"
},
"type": "library",
"autoload": {
"psr-4": {
"League\\Flysystem\\Local\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Frank de Jonge",
"email": "info@frankdejonge.nl"
}
],
"description": "Local filesystem adapter for Flysystem.",
"keywords": [
"Flysystem",
"file",
"files",
"filesystem",
"local"
],
"support": {
"issues": "https://github.com/thephpleague/flysystem-local/issues",
"source": "https://github.com/thephpleague/flysystem-local/tree/3.15.0"
},
"funding": [
{
"url": "https://ecologi.com/frankdejonge",
"type": "custom"
},
{
"url": "https://github.com/frankdejonge",
"type": "github"
}
],
"time": "2023-05-02T20:02:14+00:00"
},
{
"name": "league/mime-type-detection",
@ -3770,16 +3971,16 @@
},
{
"name": "psy/psysh",
"version": "v0.11.16",
"version": "v0.11.17",
"source": {
"type": "git",
"url": "https://github.com/bobthecow/psysh.git",
"reference": "151b145906804eea8e5d71fea23bfb470c904bfb"
"reference": "3dc5d4018dabd80bceb8fe1e3191ba8460569f0a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/bobthecow/psysh/zipball/151b145906804eea8e5d71fea23bfb470c904bfb",
"reference": "151b145906804eea8e5d71fea23bfb470c904bfb",
"url": "https://api.github.com/repos/bobthecow/psysh/zipball/3dc5d4018dabd80bceb8fe1e3191ba8460569f0a",
"reference": "3dc5d4018dabd80bceb8fe1e3191ba8460569f0a",
"shasum": ""
},
"require": {
@ -3840,9 +4041,9 @@
],
"support": {
"issues": "https://github.com/bobthecow/psysh/issues",
"source": "https://github.com/bobthecow/psysh/tree/v0.11.16"
"source": "https://github.com/bobthecow/psysh/tree/v0.11.17"
},
"time": "2023-04-26T12:53:57+00:00"
"time": "2023-05-05T20:02:42+00:00"
},
{
"name": "ralouphie/getallheaders",
@ -4224,16 +4425,16 @@
},
{
"name": "spatie/laravel-data",
"version": "3.4.4",
"version": "3.5.0",
"source": {
"type": "git",
"url": "https://github.com/spatie/laravel-data.git",
"reference": "d0208dbc6d49ca44ef3ba0dd542cd83ba15bcbc4"
"reference": "4c3c31d7d9a515125bfa219099c4df563e4422a8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/spatie/laravel-data/zipball/d0208dbc6d49ca44ef3ba0dd542cd83ba15bcbc4",
"reference": "d0208dbc6d49ca44ef3ba0dd542cd83ba15bcbc4",
"url": "https://api.github.com/repos/spatie/laravel-data/zipball/4c3c31d7d9a515125bfa219099c4df563e4422a8",
"reference": "4c3c31d7d9a515125bfa219099c4df563e4422a8",
"shasum": ""
},
"require": {
@ -4295,7 +4496,7 @@
],
"support": {
"issues": "https://github.com/spatie/laravel-data/issues",
"source": "https://github.com/spatie/laravel-data/tree/3.4.4"
"source": "https://github.com/spatie/laravel-data/tree/3.5.0"
},
"funding": [
{
@ -4303,7 +4504,7 @@
"type": "github"
}
],
"time": "2023-04-14T08:24:46+00:00"
"time": "2023-05-05T15:24:41+00:00"
},
{
"name": "spatie/laravel-package-tools",
@ -7667,16 +7868,16 @@
"packages-dev": [
{
"name": "brianium/paratest",
"version": "v7.1.3",
"version": "v7.1.4",
"source": {
"type": "git",
"url": "https://github.com/paratestphp/paratest.git",
"reference": "f394bb33b2bb7a4120b531e8991409b7aa62fc43"
"reference": "153e68eb9e697baa3acf1db1d8ae1d1eb19fb816"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/paratestphp/paratest/zipball/f394bb33b2bb7a4120b531e8991409b7aa62fc43",
"reference": "f394bb33b2bb7a4120b531e8991409b7aa62fc43",
"url": "https://api.github.com/repos/paratestphp/paratest/zipball/153e68eb9e697baa3acf1db1d8ae1d1eb19fb816",
"reference": "153e68eb9e697baa3acf1db1d8ae1d1eb19fb816",
"shasum": ""
},
"require": {
@ -7687,25 +7888,25 @@
"fidry/cpu-core-counter": "^0.4.1 || ^0.5.1",
"jean85/pretty-package-versions": "^2.0.5",
"php": "~8.1.0 || ~8.2.0",
"phpunit/php-code-coverage": "^10.1.0",
"phpunit/php-code-coverage": "^10.1.1",
"phpunit/php-file-iterator": "^4.0.1",
"phpunit/php-timer": "^6.0",
"phpunit/phpunit": "^10.1.0",
"phpunit/phpunit": "^10.1.2",
"sebastian/environment": "^6.0.1",
"symfony/console": "^6.2.8",
"symfony/process": "^6.2.8"
"symfony/console": "^6.2.10",
"symfony/process": "^6.2.10"
},
"require-dev": {
"doctrine/coding-standard": "^11.1.0",
"doctrine/coding-standard": "^12.0.0",
"ext-pcov": "*",
"ext-posix": "*",
"infection/infection": "^0.26.19",
"phpstan/phpstan": "^1.10.13",
"infection/infection": "^0.26.21",
"phpstan/phpstan": "^1.10.14",
"phpstan/phpstan-deprecation-rules": "^1.1.3",
"phpstan/phpstan-phpunit": "^1.3.11",
"phpstan/phpstan-strict-rules": "^1.5.1",
"squizlabs/php_codesniffer": "^3.7.2",
"symfony/filesystem": "^6.2.7"
"symfony/filesystem": "^6.2.10"
},
"bin": [
"bin/paratest",
@ -7746,7 +7947,7 @@
],
"support": {
"issues": "https://github.com/paratestphp/paratest/issues",
"source": "https://github.com/paratestphp/paratest/tree/v7.1.3"
"source": "https://github.com/paratestphp/paratest/tree/v7.1.4"
},
"funding": [
{
@ -7758,7 +7959,7 @@
"type": "paypal"
}
],
"time": "2023-04-14T06:17:37+00:00"
"time": "2023-05-05T09:09:30+00:00"
},
{
"name": "fakerphp/faker",
@ -9062,16 +9263,16 @@
},
{
"name": "phpunit/php-file-iterator",
"version": "4.0.1",
"version": "4.0.2",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-file-iterator.git",
"reference": "fd9329ab3368f59fe1fe808a189c51086bd4b6bd"
"reference": "5647d65443818959172645e7ed999217360654b6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/fd9329ab3368f59fe1fe808a189c51086bd4b6bd",
"reference": "fd9329ab3368f59fe1fe808a189c51086bd4b6bd",
"url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/5647d65443818959172645e7ed999217360654b6",
"reference": "5647d65443818959172645e7ed999217360654b6",
"shasum": ""
},
"require": {
@ -9110,7 +9311,8 @@
],
"support": {
"issues": "https://github.com/sebastianbergmann/php-file-iterator/issues",
"source": "https://github.com/sebastianbergmann/php-file-iterator/tree/4.0.1"
"security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy",
"source": "https://github.com/sebastianbergmann/php-file-iterator/tree/4.0.2"
},
"funding": [
{
@ -9118,7 +9320,7 @@
"type": "github"
}
],
"time": "2023-02-10T16:53:14+00:00"
"time": "2023-05-07T09:13:23+00:00"
},
{
"name": "phpunit/php-invoker",
@ -10428,16 +10630,16 @@
},
{
"name": "spatie/ignition",
"version": "1.6.0",
"version": "1.7.0",
"source": {
"type": "git",
"url": "https://github.com/spatie/ignition.git",
"reference": "fbcfcabc44e506e40c4d72fd4ddf465e272a600e"
"reference": "f747d83c6d7cb6229b462f3ddbb3a82dc0db0f78"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/spatie/ignition/zipball/fbcfcabc44e506e40c4d72fd4ddf465e272a600e",
"reference": "fbcfcabc44e506e40c4d72fd4ddf465e272a600e",
"url": "https://api.github.com/repos/spatie/ignition/zipball/f747d83c6d7cb6229b462f3ddbb3a82dc0db0f78",
"reference": "f747d83c6d7cb6229b462f3ddbb3a82dc0db0f78",
"shasum": ""
},
"require": {
@ -10507,20 +10709,20 @@
"type": "github"
}
],
"time": "2023-04-27T08:40:07+00:00"
"time": "2023-05-04T13:20:26+00:00"
},
{
"name": "spatie/laravel-ignition",
"version": "2.1.0",
"version": "2.1.2",
"source": {
"type": "git",
"url": "https://github.com/spatie/laravel-ignition.git",
"reference": "3718dfb91bc5aff340af26507a61f0f9605f81e8"
"reference": "2f99fa6b732a6049e78ed34e4608ce589605ae54"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/spatie/laravel-ignition/zipball/3718dfb91bc5aff340af26507a61f0f9605f81e8",
"reference": "3718dfb91bc5aff340af26507a61f0f9605f81e8",
"url": "https://api.github.com/repos/spatie/laravel-ignition/zipball/2f99fa6b732a6049e78ed34e4608ce589605ae54",
"reference": "2f99fa6b732a6049e78ed34e4608ce589605ae54",
"shasum": ""
},
"require": {
@ -10599,7 +10801,7 @@
"type": "github"
}
],
"time": "2023-04-12T09:26:00+00:00"
"time": "2023-05-09T07:19:31+00:00"
},
{
"name": "symfony/http-client",

View File

@ -194,6 +194,7 @@
App\Providers\AuthServiceProvider::class,
// App\Providers\BroadcastServiceProvider::class,
App\Providers\EventServiceProvider::class,
App\Providers\HorizonServiceProvider::class,
App\Providers\RouteServiceProvider::class,
],

View File

@ -4,4 +4,6 @@
'version' => '4.0.0-nightly.2',
'mux_enabled' => env('MUX_ENABLED', true),
'dev_webhook' => env('SERVEO_URL'),
];

215
config/horizon.php Normal file
View File

@ -0,0 +1,215 @@
<?php
use Illuminate\Support\Str;
return [
/*
|--------------------------------------------------------------------------
| Horizon Domain
|--------------------------------------------------------------------------
|
| This is the subdomain where Horizon will be accessible from. If this
| setting is null, Horizon will reside under the same domain as the
| application. Otherwise, this value will serve as the subdomain.
|
*/
'domain' => env('HORIZON_DOMAIN'),
/*
|--------------------------------------------------------------------------
| Horizon Path
|--------------------------------------------------------------------------
|
| This is the URI path where Horizon will be accessible from. Feel free
| to change this path to anything you like. Note that the URI will not
| affect the paths of its internal API that aren't exposed to users.
|
*/
'path' => env('HORIZON_PATH', 'horizon'),
/*
|--------------------------------------------------------------------------
| Horizon Redis Connection
|--------------------------------------------------------------------------
|
| This is the name of the Redis connection where Horizon will store the
| meta information required for it to function. It includes the list
| of supervisors, failed jobs, job metrics, and other information.
|
*/
'use' => 'default',
/*
|--------------------------------------------------------------------------
| Horizon Redis Prefix
|--------------------------------------------------------------------------
|
| This prefix will be used when storing all Horizon data in Redis. You
| may modify the prefix when you are running multiple installations
| of Horizon on the same server so that they don't have problems.
|
*/
'prefix' => env(
'HORIZON_PREFIX',
Str::slug(env('APP_NAME', 'laravel'), '_') . '_horizon:'
),
/*
|--------------------------------------------------------------------------
| Horizon Route Middleware
|--------------------------------------------------------------------------
|
| These middleware will get attached onto each Horizon route, giving you
| the chance to add your own middleware to this list or change any of
| the existing middleware. Or, you can simply stick with this list.
|
*/
'middleware' => ['web'],
/*
|--------------------------------------------------------------------------
| Queue Wait Time Thresholds
|--------------------------------------------------------------------------
|
| This option allows you to configure when the LongWaitDetected event
| will be fired. Every connection / queue combination may have its
| own, unique threshold (in seconds) before this event is fired.
|
*/
'waits' => [
'redis:default' => 60,
],
/*
|--------------------------------------------------------------------------
| Job Trimming Times
|--------------------------------------------------------------------------
|
| Here you can configure for how long (in minutes) you desire Horizon to
| persist the recent and failed jobs. Typically, recent jobs are kept
| for one hour while all failed jobs are stored for an entire week.
|
*/
'trim' => [
'recent' => 60,
'pending' => 60,
'completed' => 60,
'recent_failed' => 10080,
'failed' => 10080,
'monitored' => 10080,
],
/*
|--------------------------------------------------------------------------
| Silenced Jobs
|--------------------------------------------------------------------------
|
| Silencing a job will instruct Horizon to not place the job in the list
| of completed jobs within the Horizon dashboard. This setting may be
| used to fully remove any noisy jobs from the completed jobs list.
|
*/
'silenced' => [
// App\Jobs\ExampleJob::class,
],
/*
|--------------------------------------------------------------------------
| Metrics
|--------------------------------------------------------------------------
|
| Here you can configure how many snapshots should be kept to display in
| the metrics graph. This will get used in combination with Horizon's
| `horizon:snapshot` schedule to define how long to retain metrics.
|
*/
'metrics' => [
'trim_snapshots' => [
'job' => 24,
'queue' => 24,
],
],
/*
|--------------------------------------------------------------------------
| Fast Termination
|--------------------------------------------------------------------------
|
| When this option is enabled, Horizon's "terminate" command will not
| wait on all of the workers to terminate unless the --wait option
| is provided. Fast termination can shorten deployment delay by
| allowing a new instance of Horizon to start while the last
| instance will continue to terminate each of its workers.
|
*/
'fast_termination' => false,
/*
|--------------------------------------------------------------------------
| Memory Limit (MB)
|--------------------------------------------------------------------------
|
| This value describes the maximum amount of memory the Horizon master
| supervisor may consume before it is terminated and restarted. For
| configuring these limits on your workers, see the next section.
|
*/
'memory_limit' => 64,
/*
|--------------------------------------------------------------------------
| Queue Worker Configuration
|--------------------------------------------------------------------------
|
| Here you may define the queue worker settings used by your application
| in all environments. These supervisors and settings handle all your
| queued jobs and will be provisioned by Horizon during deployment.
|
*/
'defaults' => [
'supervisor-1' => [
'connection' => 'redis',
'queue' => ['default'],
'balance' => 'auto',
'autoScalingStrategy' => 'time',
'maxProcesses' => 1,
'maxTime' => 0,
'maxJobs' => 0,
'memory' => 128,
'tries' => 1,
'timeout' => 600,
'nice' => 0,
],
],
'environments' => [
'production' => [
'supervisor-1' => [
'maxProcesses' => env('HORIZON_MAX_PROCESSES', 10),
'balanceMaxShift' => env('HORIZON_BALANCE_MAX_SHIFT', 1),
'balanceCooldown' => env('HORIZON_BALANCE_COOLDOWN', 3),
],
],
'local' => [
'supervisor-1' => [
'maxProcesses' => env('HORIZON_MAX_PROCESSES', 10),
'balanceMaxShift' => env('HORIZON_BALANCE_MAX_SHIFT', 1),
'balanceCooldown' => env('HORIZON_BALANCE_COOLDOWN', 3),
],
],
],
];

3
config/version.php Normal file
View File

@ -0,0 +1,3 @@
<?php
return '4.0.0-nightly.3';

View File

@ -1,32 +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('failed_jobs', function (Blueprint $table) {
$table->id();
$table->string('uuid')->unique();
$table->text('connection');
$table->text('queue');
$table->longText('payload');
$table->longText('exception');
$table->timestamp('failed_at')->useCurrent();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('failed_jobs');
}
};

View File

@ -1,32 +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('jobs', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('queue')->index();
$table->longText('payload');
$table->unsignedTinyInteger('attempts');
$table->unsignedInteger('reserved_at')->nullable();
$table->unsignedInteger('available_at');
$table->unsignedInteger('created_at');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('jobs');
}
};

View File

@ -15,6 +15,7 @@ public function up(): void
$table->id();
$table->string('uuid')->unique();
$table->boolean('is_build_server')->default(false);
$table->boolean('is_validated')->default(false);
$table->foreignId('server_id');
$table->timestamps();

View File

@ -13,6 +13,7 @@ public function up(): void
{
Schema::create('applications', function (Blueprint $table) {
$table->id();
$table->integer('repository_project_id')->nullable();
$table->string('uuid')->unique();
$table->string('name');
@ -21,7 +22,8 @@ public function up(): void
$table->string('git_repository');
$table->string('git_branch');
$table->string('git_commit_sha')->nullable();
$table->string('git_commit_sha')->default('HEAD');
$table->string('git_full_url')->nullable();
$table->string('docker_registry_image_name')->nullable();
$table->string('docker_registry_image_tag')->nullable();
@ -54,8 +56,9 @@ public function up(): void
$table->string('status')->default('exited');
$table->nullableMorphs('destination');
$table->morphs('source');
$table->nullableMorphs('source');
$table->foreignId('private_key_id')->nullable();
$table->foreignId('environment_id');
$table->timestamps();
});

View File

@ -19,8 +19,8 @@ public function up(): void
$table->string('organization')->nullable();
$table->string('api_url');
$table->string('html_url');
$table->integer('custom_port')->default(22);
$table->string('custom_user')->default('git');
$table->integer('custom_port')->default(22);
$table->integer('app_id')->nullable();
$table->integer('installation_id')->nullable();

View File

@ -24,7 +24,8 @@ public function run(): void
$github_public_source = GithubApp::where('name', 'Public GitHub')->first();
Application::create([
'name' => 'Public application (from GitHub)',
'name' => 'coollabsio/coolify-examples:nodejs-fastify',
'repository_project_id' => 603035348,
'git_repository' => 'coollabsio/coolify-examples',
'git_branch' => 'nodejs-fastify',
'build_pack' => 'nixpacks',

View File

@ -26,12 +26,13 @@ public function run(): void
'team_id' => $root_team->id,
]);
GithubApp::create([
'name' => 'coolify-laravel-development-private-github',
'name' => 'coolify-laravel-development-public',
'uuid' => '69420',
'api_url' => 'https://api.github.com',
'html_url' => 'https://github.com',
'is_public' => false,
'app_id' => 292941,
'installation_id' => 34157139,
'installation_id' => 37267016,
'client_id' => 'Iv1.220e564d2b0abd8c',
'client_secret' => '96b1b31f36ce0a34386d11798ff35b9b6d8aba3a',
'webhook_secret' => '326a47b49054f03288f800d81247ec9414d0abf3',

View File

@ -13,11 +13,10 @@ class LocalPersistentVolumeSeeder extends Seeder
*/
public function run(): void
{
$application = Application::where('name', 'Public application (from GitHub)')->first();
LocalPersistentVolume::create([
'name' => 'test-pv',
'mount_path' => '/data',
'resource_id' => $application->id,
'resource_id' => 1,
'resource_type' => Application::class,
]);
}

View File

@ -21,6 +21,26 @@ public function run(): void
'id' => 0
]);
}
if (GithubApp::find(0) == null) {
GithubApp::create([
'id' => 0,
'name' => 'Public GitHub',
'api_url' => 'https://api.github.com',
'html_url' => 'https://github.com',
'is_public' => true,
'team_id' => 0,
]);
}
if (GitlabApp::find(0) == null) {
GitlabApp::create([
'id' => 0,
'name' => 'Public GitLab',
'api_url' => 'https://gitlab.com/api/v4',
'html_url' => 'https://gitlab.com',
'is_public' => true,
'team_id' => 0,
]);
}
// Add first Team if it doesn't exist
if (Team::find(0) == null) {
@ -50,6 +70,7 @@ public function run(): void
$private_key->save();
}
} else {
// TODO: Add a command to generate a new SSH key for the Coolify host machine (localhost).
echo "No SSH key found for the Coolify host machine (localhost).\n";
echo "Please generate one and save it in storage/app/ssh-keys/{$coolify_key_name}\n";
exit(1);
@ -57,7 +78,7 @@ public function run(): void
// Add Coolify host (localhost) as Server if it doesn't exist
if (Server::find(0) == null) {
Server::create([
$server = Server::create([
'id' => 0,
'name' => "localhost",
'description' => "This is the local machine",
@ -66,26 +87,8 @@ public function run(): void
'team_id' => 0,
'private_key_id' => 0,
]);
}
if (GithubApp::find(0) == null) {
GithubApp::create([
'id' => 0,
'name' => 'Public GitHub',
'api_url' => 'https://api.github.com',
'html_url' => 'https://github.com',
'is_public' => true,
'team_id' => 0,
]);
}
if (GitlabApp::find(0) == null) {
GitlabApp::create([
'id' => 0,
'name' => 'Public GitLab',
'api_url' => 'https://gitlab.com/api/v4',
'html_url' => 'https://gitlab.com',
'is_public' => true,
'team_id' => 0,
]);
$server->settings->is_validated = true;
$server->settings->save();
}
}
}

View File

@ -15,11 +15,12 @@ public function run(): void
{
$server_2 = Server::find(1)->load(['settings']);
$server_2->settings->is_build_server = true;
$server_2->settings->is_validated = true;
$server_2->settings->save();
$server_3 = Server::find(2)->load(['settings']);
$server_3->settings->is_build_server = true;
$server_3->settings->is_build_server = false;
$server_3->settings->is_validated = false;
$server_3->settings->save();
}
}

View File

@ -1,6 +1,7 @@
version: '3.8'
x-testing-host: &testing-host-base
x-testing-host:
&testing-host-base
image: coolify-testing-host
build:
dockerfile: Dockerfile
@ -8,7 +9,6 @@ x-testing-host: &testing-host-base
networks:
- coolify
services:
coolify:
build:
@ -38,6 +38,13 @@ services:
POSTGRES_HOST_AUTH_METHOD: "trust"
volumes:
- ./_volumes/database/:/var/lib/postgresql/data
redis:
ports:
- "${FORWARD_REDIS_PORT:-6379}:6379"
env_file:
- .env
volumes:
- ./_volumes/redis/:/data
vite:
image: node:19
working_dir: /var/www/html
@ -47,16 +54,13 @@ services:
- .:/var/www/html:cached
command: sh -c "npm install && npm run dev"
testing-host:
<<: *testing-host-base
container_name: coolify-testing-host
volumes:
- /var/run/docker.sock:/var/run/docker.sock
<<: *testing-host-base
container_name: coolify-testing-host
volumes:
- /var/run/docker.sock:/var/run/docker.sock
testing-host-2:
<<: *testing-host-base
container_name: coolify-testing-host-2
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- "./_testing_hosts/host_2_proxy:/root/projects/proxy"
<<: *testing-host-base
container_name: coolify-testing-host-2
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- "./_testing_hosts/host_2_proxy:/root/projects/proxy"

View File

@ -2,7 +2,6 @@ version: '3.8'
services:
coolify:
image: "ghcr.io/coollabsio/coolify:${APP_TAG:-4.0.0-nightly.0}"
container_name: coolify
volumes:
- type: bind
source: /data/coolify/source/.env
@ -24,7 +23,12 @@ services:
- DB_USERNAME
- DB_PASSWORD
- QUEUE_CONNECTION
- REDIS_HOST
- REDIS_PASSWORD
- SSL_MODE=off
- PHP_PM_CONTROL=dynamic
- PHP_PM_START_SERVERS=5
- PHP_PM_MAX_SPARE_SERVERS=10
ports:
- "${APP_PORT:-8000}:80"
expose:
@ -38,7 +42,6 @@ services:
postgres:
condition: service_healthy
postgres:
container_name: coolify-db
volumes:
- coolify-db:/var/lib/postgresql/data
environment:
@ -56,6 +59,19 @@ services:
interval: 2s
retries: 5
timeout: 2s
redis:
command: redis-server --save 20 1 --loglevel warning --requirepass ${REDIS_PASSWORD}
environment:
REDIS_PASSWORD: "${REDIS_PASSWORD}"
volumes:
- coolify-redis:/data
healthcheck:
test: redis-cli ping
interval: 2s
retries: 5
timeout: 2s
volumes:
coolify-db:
name: coolify-db
coolify-redis:
name: coolify-redis

View File

@ -1,18 +1,28 @@
version: '3.8'
services:
coolify:
working_dir: /var/www/html
extra_hosts:
- 'host.docker.internal:host-gateway'
networks:
- coolify
depends_on:
- postgres
coolify:
container_name: coolify
restart: always
working_dir: /var/www/html
extra_hosts:
- 'host.docker.internal:host-gateway'
networks:
- coolify
depends_on:
- postgres
postgres:
image: postgres:15-alpine
networks:
- coolify
postgres:
image: postgres:15-alpine
container_name: coolify-db
restart: always
networks:
- coolify
redis:
image: redis:alpine
container_name: coolify-redis
restart: always
networks:
- coolify
networks:
coolify:
driver: bridge
coolify:
driver: bridge

View File

@ -0,0 +1,2 @@
#!/command/execlineb -P
su - webuser -c "php /var/www/html/artisan horizon"

View File

@ -0,0 +1 @@
longrun

View File

@ -1,2 +1,2 @@
#!/command/execlineb -P
su - webuser -c "php /var/www/html/artisan queue:work --timeout=600 --memory=512"
su - webuser -c "php /var/www/html/artisan queue:work --timeout=600"

268
package-lock.json generated
View File

@ -1,5 +1,5 @@
{
"name": "html",
"name": "coolify",
"lockfileVersion": 3,
"requires": true,
"packages": {
@ -8,12 +8,26 @@
"alpinejs": "3.12.0"
},
"devDependencies": {
"@vitejs/plugin-vue": "^4.0.0",
"autoprefixer": "10.4.14",
"axios": "1.3.6",
"laravel-vite-plugin": "0.7.4",
"postcss": "8.4.23",
"tailwindcss": "3.3.1",
"vite": "4.3.1"
"vite": "4.3.1",
"vue": "^3.2.37"
}
},
"node_modules/@babel/parser": {
"version": "7.21.8",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.8.tgz",
"integrity": "sha512-6zavDGdzG3gUqAdWvlLFfk+36RilI+Pwyuuh7HItyeScCWP3k6i8vKclAQ0bM/0y/Kz/xiwvxhMv9MgTJP5gmA==",
"dev": true,
"bin": {
"parser": "bin/babel-parser.js"
},
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/@esbuild/android-arm": {
@ -457,6 +471,93 @@
"node": ">= 8"
}
},
"node_modules/@vitejs/plugin-vue": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-4.2.1.tgz",
"integrity": "sha512-ZTZjzo7bmxTRTkb8GSTwkPOYDIP7pwuyV+RV53c9PYUouwcbkIZIvWvNWlX2b1dYZqtOv7D6iUAnJLVNGcLrSw==",
"dev": true,
"engines": {
"node": "^14.18.0 || >=16.0.0"
},
"peerDependencies": {
"vite": "^4.0.0",
"vue": "^3.2.25"
}
},
"node_modules/@vue/compiler-core": {
"version": "3.2.47",
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.47.tgz",
"integrity": "sha512-p4D7FDnQb7+YJmO2iPEv0SQNeNzcbHdGByJDsT4lynf63AFkOTFN07HsiRSvjGo0QrxR/o3d0hUyNCUnBU2Tig==",
"dev": true,
"dependencies": {
"@babel/parser": "^7.16.4",
"@vue/shared": "3.2.47",
"estree-walker": "^2.0.2",
"source-map": "^0.6.1"
}
},
"node_modules/@vue/compiler-core/node_modules/@vue/shared": {
"version": "3.2.47",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.47.tgz",
"integrity": "sha512-BHGyyGN3Q97EZx0taMQ+OLNuZcW3d37ZEVmEAyeoA9ERdGvm9Irc/0Fua8SNyOtV1w6BS4q25wbMzJujO9HIfQ==",
"dev": true
},
"node_modules/@vue/compiler-dom": {
"version": "3.2.47",
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.2.47.tgz",
"integrity": "sha512-dBBnEHEPoftUiS03a4ggEig74J2YBZ2UIeyfpcRM2tavgMWo4bsEfgCGsu+uJIL/vax9S+JztH8NmQerUo7shQ==",
"dev": true,
"dependencies": {
"@vue/compiler-core": "3.2.47",
"@vue/shared": "3.2.47"
}
},
"node_modules/@vue/compiler-dom/node_modules/@vue/shared": {
"version": "3.2.47",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.47.tgz",
"integrity": "sha512-BHGyyGN3Q97EZx0taMQ+OLNuZcW3d37ZEVmEAyeoA9ERdGvm9Irc/0Fua8SNyOtV1w6BS4q25wbMzJujO9HIfQ==",
"dev": true
},
"node_modules/@vue/compiler-sfc": {
"version": "3.2.47",
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.2.47.tgz",
"integrity": "sha512-rog05W+2IFfxjMcFw10tM9+f7i/+FFpZJJ5XHX72NP9eC2uRD+42M3pYcQqDXVYoj74kHMSEdQ/WmCjt8JFksQ==",
"dev": true,
"dependencies": {
"@babel/parser": "^7.16.4",
"@vue/compiler-core": "3.2.47",
"@vue/compiler-dom": "3.2.47",
"@vue/compiler-ssr": "3.2.47",
"@vue/reactivity-transform": "3.2.47",
"@vue/shared": "3.2.47",
"estree-walker": "^2.0.2",
"magic-string": "^0.25.7",
"postcss": "^8.1.10",
"source-map": "^0.6.1"
}
},
"node_modules/@vue/compiler-sfc/node_modules/@vue/shared": {
"version": "3.2.47",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.47.tgz",
"integrity": "sha512-BHGyyGN3Q97EZx0taMQ+OLNuZcW3d37ZEVmEAyeoA9ERdGvm9Irc/0Fua8SNyOtV1w6BS4q25wbMzJujO9HIfQ==",
"dev": true
},
"node_modules/@vue/compiler-ssr": {
"version": "3.2.47",
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.2.47.tgz",
"integrity": "sha512-wVXC+gszhulcMD8wpxMsqSOpvDZ6xKXSVWkf50Guf/S+28hTAXPDYRTbLQ3EDkOP5Xz/+SY37YiwDquKbJOgZw==",
"dev": true,
"dependencies": {
"@vue/compiler-dom": "3.2.47",
"@vue/shared": "3.2.47"
}
},
"node_modules/@vue/compiler-ssr/node_modules/@vue/shared": {
"version": "3.2.47",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.47.tgz",
"integrity": "sha512-BHGyyGN3Q97EZx0taMQ+OLNuZcW3d37ZEVmEAyeoA9ERdGvm9Irc/0Fua8SNyOtV1w6BS4q25wbMzJujO9HIfQ==",
"dev": true
},
"node_modules/@vue/reactivity": {
"version": "3.1.5",
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.1.5.tgz",
@ -465,6 +566,86 @@
"@vue/shared": "3.1.5"
}
},
"node_modules/@vue/reactivity-transform": {
"version": "3.2.47",
"resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.2.47.tgz",
"integrity": "sha512-m8lGXw8rdnPVVIdIFhf0LeQ/ixyHkH5plYuS83yop5n7ggVJU+z5v0zecwEnX7fa7HNLBhh2qngJJkxpwEEmYA==",
"dev": true,
"dependencies": {
"@babel/parser": "^7.16.4",
"@vue/compiler-core": "3.2.47",
"@vue/shared": "3.2.47",
"estree-walker": "^2.0.2",
"magic-string": "^0.25.7"
}
},
"node_modules/@vue/reactivity-transform/node_modules/@vue/shared": {
"version": "3.2.47",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.47.tgz",
"integrity": "sha512-BHGyyGN3Q97EZx0taMQ+OLNuZcW3d37ZEVmEAyeoA9ERdGvm9Irc/0Fua8SNyOtV1w6BS4q25wbMzJujO9HIfQ==",
"dev": true
},
"node_modules/@vue/runtime-core": {
"version": "3.2.47",
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.2.47.tgz",
"integrity": "sha512-RZxbLQIRB/K0ev0K9FXhNbBzT32H9iRtYbaXb0ZIz2usLms/D55dJR2t6cIEUn6vyhS3ALNvNthI+Q95C+NOpA==",
"dev": true,
"dependencies": {
"@vue/reactivity": "3.2.47",
"@vue/shared": "3.2.47"
}
},
"node_modules/@vue/runtime-core/node_modules/@vue/reactivity": {
"version": "3.2.47",
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.2.47.tgz",
"integrity": "sha512-7khqQ/75oyyg+N/e+iwV6lpy1f5wq759NdlS1fpAhFXa8VeAIKGgk2E/C4VF59lx5b+Ezs5fpp/5WsRYXQiKxQ==",
"dev": true,
"dependencies": {
"@vue/shared": "3.2.47"
}
},
"node_modules/@vue/runtime-core/node_modules/@vue/shared": {
"version": "3.2.47",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.47.tgz",
"integrity": "sha512-BHGyyGN3Q97EZx0taMQ+OLNuZcW3d37ZEVmEAyeoA9ERdGvm9Irc/0Fua8SNyOtV1w6BS4q25wbMzJujO9HIfQ==",
"dev": true
},
"node_modules/@vue/runtime-dom": {
"version": "3.2.47",
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.2.47.tgz",
"integrity": "sha512-ArXrFTjS6TsDei4qwNvgrdmHtD930KgSKGhS5M+j8QxXrDJYLqYw4RRcDy1bz1m1wMmb6j+zGLifdVHtkXA7gA==",
"dev": true,
"dependencies": {
"@vue/runtime-core": "3.2.47",
"@vue/shared": "3.2.47",
"csstype": "^2.6.8"
}
},
"node_modules/@vue/runtime-dom/node_modules/@vue/shared": {
"version": "3.2.47",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.47.tgz",
"integrity": "sha512-BHGyyGN3Q97EZx0taMQ+OLNuZcW3d37ZEVmEAyeoA9ERdGvm9Irc/0Fua8SNyOtV1w6BS4q25wbMzJujO9HIfQ==",
"dev": true
},
"node_modules/@vue/server-renderer": {
"version": "3.2.47",
"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.2.47.tgz",
"integrity": "sha512-dN9gc1i8EvmP9RCzvneONXsKfBRgqFeFZLurmHOveL7oH6HiFXJw5OGu294n1nHc/HMgTy6LulU/tv5/A7f/LA==",
"dev": true,
"dependencies": {
"@vue/compiler-ssr": "3.2.47",
"@vue/shared": "3.2.47"
},
"peerDependencies": {
"vue": "3.2.47"
}
},
"node_modules/@vue/server-renderer/node_modules/@vue/shared": {
"version": "3.2.47",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.47.tgz",
"integrity": "sha512-BHGyyGN3Q97EZx0taMQ+OLNuZcW3d37ZEVmEAyeoA9ERdGvm9Irc/0Fua8SNyOtV1w6BS4q25wbMzJujO9HIfQ==",
"dev": true
},
"node_modules/@vue/shared": {
"version": "3.1.5",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.1.5.tgz",
@ -727,6 +908,12 @@
"node": ">=4"
}
},
"node_modules/csstype": {
"version": "2.6.21",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.21.tgz",
"integrity": "sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w==",
"dev": true
},
"node_modules/delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
@ -800,6 +987,12 @@
"node": ">=6"
}
},
"node_modules/estree-walker": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
"dev": true
},
"node_modules/fast-glob": {
"version": "3.2.12",
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz",
@ -966,6 +1159,14 @@
"node": ">= 0.4.0"
}
},
"node_modules/immutable": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.0.tgz",
"integrity": "sha512-0AOCmOip+xgJwEVTQj1EfiDDOkPmuyllDuTuEX+DDXUgapLAsBIfkg3sxCYyCEA8mQqZrrxPUGjcOQ2JS3WLkg==",
"dev": true,
"optional": true,
"peer": true
},
"node_modules/inflight": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
@ -1076,6 +1277,15 @@
"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
"dev": true
},
"node_modules/magic-string": {
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz",
"integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==",
"dev": true,
"dependencies": {
"sourcemap-codec": "^1.4.8"
}
},
"node_modules/merge2": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
@ -1518,6 +1728,34 @@
"queue-microtask": "^1.2.2"
}
},
"node_modules/sass": {
"version": "1.62.1",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.62.1.tgz",
"integrity": "sha512-NHpxIzN29MXvWiuswfc1W3I0N8SXBd8UR26WntmDlRYf0bSADnwnOjsyMZ3lMezSlArD33Vs3YFhp7dWvL770A==",
"dev": true,
"optional": true,
"peer": true,
"dependencies": {
"chokidar": ">=3.0.0 <4.0.0",
"immutable": "^4.0.0",
"source-map-js": ">=0.6.2 <2.0.0"
},
"bin": {
"sass": "sass.js"
},
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true,
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/source-map-js": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
@ -1527,6 +1765,13 @@
"node": ">=0.10.0"
}
},
"node_modules/sourcemap-codec": {
"version": "1.4.8",
"resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz",
"integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==",
"deprecated": "Please use @jridgewell/sourcemap-codec instead",
"dev": true
},
"node_modules/sucrase": {
"version": "3.32.0",
"resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.32.0.tgz",
@ -1735,6 +1980,25 @@
"vite": "^2 || ^3 || ^4"
}
},
"node_modules/vue": {
"version": "3.2.47",
"resolved": "https://registry.npmjs.org/vue/-/vue-3.2.47.tgz",
"integrity": "sha512-60188y/9Dc9WVrAZeUVSDxRQOZ+z+y5nO2ts9jWXSTkMvayiWxCWOWtBQoYjLeccfXkiiPZWAHcV+WTPhkqJHQ==",
"dev": true,
"dependencies": {
"@vue/compiler-dom": "3.2.47",
"@vue/compiler-sfc": "3.2.47",
"@vue/runtime-dom": "3.2.47",
"@vue/server-renderer": "3.2.47",
"@vue/shared": "3.2.47"
}
},
"node_modules/vue/node_modules/@vue/shared": {
"version": "3.2.47",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.47.tgz",
"integrity": "sha512-BHGyyGN3Q97EZx0taMQ+OLNuZcW3d37ZEVmEAyeoA9ERdGvm9Irc/0Fua8SNyOtV1w6BS4q25wbMzJujO9HIfQ==",
"dev": true
},
"node_modules/wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",

View File

@ -5,12 +5,14 @@
"build": "vite build"
},
"devDependencies": {
"@vitejs/plugin-vue": "^4.0.0",
"autoprefixer": "10.4.14",
"axios": "1.3.6",
"laravel-vite-plugin": "0.7.4",
"postcss": "8.4.23",
"tailwindcss": "3.3.1",
"vite": "4.3.1"
"vite": "4.3.1",
"vue": "^3.2.37"
},
"dependencies": {
"alpinejs": "3.12.0"

8
public/vendor/horizon/app-dark.css vendored Normal file

File diff suppressed because one or more lines are too long

8
public/vendor/horizon/app.css vendored Normal file

File diff suppressed because one or more lines are too long

2
public/vendor/horizon/app.js vendored Normal file

File diff suppressed because one or more lines are too long

BIN
public/vendor/horizon/img/favicon.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 648 B

4
public/vendor/horizon/img/horizon.svg vendored Normal file
View File

@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="226" height="30" viewBox="0 0 226 30">
<path fill="#405263" d="M54.132 25l-.644-4.144h-5.936V5.68h-4.788V25h11.368zm13.3 0H63.68l-.28-1.708c-1.12 1.176-2.436 1.988-4.312 1.988-2.184 0-3.724-1.316-3.724-3.808 0-3.22 2.464-4.732 7.84-5.208v-.252c0-1.204-.672-1.736-2.072-1.736-1.54 0-2.968.392-4.424 1.036l-.504-3.192c1.568-.644 3.332-1.064 5.572-1.064 3.78 0 5.656 1.26 5.656 4.76V25zm-4.228-3.78v-2.912c-2.884.448-3.668 1.4-3.668 2.604 0 .868.532 1.344 1.316 1.344.812 0 1.596-.364 2.352-1.036zm15.876-5.908L78.38 11c-2.016.14-3.164 1.428-4.004 2.996l-.448-2.716h-3.696V25h4.228v-7.616c1.036-1.12 2.576-1.932 4.62-2.072zM92.184 25h-3.752l-.28-1.708c-1.12 1.176-2.436 1.988-4.312 1.988-2.184 0-3.724-1.316-3.724-3.808 0-3.22 2.464-4.732 7.84-5.208v-.252c0-1.204-.672-1.736-2.072-1.736-1.54 0-2.968.392-4.424 1.036l-.504-3.192c1.568-.644 3.332-1.064 5.572-1.064 3.78 0 5.656 1.26 5.656 4.76V25zm-4.228-3.78v-2.912c-2.884.448-3.668 1.4-3.668 2.604 0 .868.532 1.344 1.316 1.344.812 0 1.596-.364 2.352-1.036zm19.544-9.94h-4.116l-2.492 8.484-2.548-8.68-4.536.532 4.676 13.44h4.228L107.5 11.28zm13.552 6.832c0 .42-.028.868-.084 1.092h-8.232c.224 2.156 1.512 2.996 3.36 2.996 1.512 0 2.996-.56 4.508-1.428l.42 2.968c-1.484.952-3.304 1.54-5.46 1.54-4.116 0-7.056-2.184-7.056-7.084 0-4.48 2.744-7.196 6.468-7.196 4.2 0 6.076 3.136 6.076 7.112zm-4.032-1.232c-.14-2.072-.868-3.136-2.156-3.136-1.176 0-2.016 1.036-2.156 3.136h4.312zM127.66 25V4.784l-4.256.672V25h4.256zm25.032 0V5.68h-1.932v8.344h-10.724V5.68h-1.932V25h1.932v-9.24h10.724V25h1.932zm15.876-6.608c0 4.368-2.632 6.888-6.02 6.888-3.388 0-5.936-2.52-5.936-6.888 0-4.396 2.604-6.916 5.936-6.916 3.444 0 6.02 2.52 6.02 6.916zm-1.904 0c0-3.052-1.456-5.348-4.116-5.348-2.632 0-4.06 2.212-4.06 5.348 0 3.052 1.456 5.32 4.06 5.32 2.688 0 4.116-2.184 4.116-5.32zm12.208-5.18l-.308-1.736c-2.184.056-3.724 1.512-4.704 2.996l-.42-2.716h-1.372V25h1.848v-8.316c.896-1.764 2.772-3.332 4.956-3.472zm5.264-5.964c0-.728-.588-1.344-1.316-1.344-.728 0-1.344.616-1.344 1.344 0 .728.616 1.344 1.344 1.344.728 0 1.316-.616 1.316-1.344zM183.716 25V11.756h-1.82V25h1.82zm13.748 0l-.252-1.54h-7.868l7.896-10.276v-1.428h-9.604l.252 1.54h7.224l-7.896 10.276V25h10.248zm14.476-6.608c0 4.368-2.632 6.888-6.02 6.888-3.388 0-5.936-2.52-5.936-6.888 0-4.396 2.604-6.916 5.936-6.916 3.444 0 6.02 2.52 6.02 6.916zm-1.904 0c0-3.052-1.456-5.348-4.116-5.348-2.632 0-4.06 2.212-4.06 5.348 0 3.052 1.456 5.32 4.06 5.32 2.688 0 4.116-2.184 4.116-5.32zM225.884 25v-9.464c0-2.548-1.316-4.06-3.948-4.06-1.792 0-3.304.952-4.76 2.24l-.308-1.96h-1.428V25h1.848v-9.576c1.456-1.428 2.884-2.296 4.284-2.296 1.68 0 2.464 1.036 2.464 2.744V25h1.848z"/>
<path fill="#405263" d="M5.26176342 26.4094389C2.04147988 23.6582233 0 19.5675182 0 15c0-4.1421356 1.67893219-7.89213562 4.39339828-10.60660172C7.10786438 1.67893219 10.8578644 0 15 0c8.2842712 0 15 6.71572875 15 15 0 8.2842712-6.7157288 15-15 15-3.716753 0-7.11777662-1.3517984-9.73823658-3.5905611zM4.03811305 15.9222506C5.70084247 14.4569342 6.87195416 12.5 10 12.5c5 0 5 5 10 5 3.1280454 0 4.2991572-1.9569336 5.961887-3.4222502C25.4934253 8.43417206 20.7645408 4 15 4 8.92486775 4 4 8.92486775 4 15c0 .3105915.01287248.6181765.03811305.9222506z"/>
</svg>

After

Width:  |  Height:  |  Size: 3.2 KiB

806
public/vendor/horizon/img/sprite.svg vendored Normal file
View File

@ -0,0 +1,806 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<symbol viewBox="0 0 20 20" id="zondicon-add-outline">
<path d="M11 9h4v2h-4v4H9v-4H5V9h4V5h2v4zm-1 11a10 10 0 1 1 0-20 10 10 0 0 1 0 20zm0-2a8 8 0 1 0 0-16 8 8 0 0 0 0 16z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-add-solid">
<path d="M11 9V5H9v4H5v2h4v4h2v-4h4V9h-4zm-1 11a10 10 0 1 1 0-20 10 10 0 0 1 0 20z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-adjust">
<path d="M10 2v16a8 8 0 1 0 0-16zm0 18a10 10 0 1 1 0-20 10 10 0 0 1 0 20z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-airplane">
<path d="M8.4 12H2.8L1 15H0V5h1l1.8 3h5.6L6 0h2l4.8 8H18a2 2 0 1 1 0 4h-5.2L8 20H6l2.4-8z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-align-center">
<path d="M1 1h18v2H1V1zm0 8h18v2H1V9zm0 8h18v2H1v-2zM4 5h12v2H4V5zm0 8h12v2H4v-2z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-align-justified">
<path d="M1 1h18v2H1V1zm0 8h18v2H1V9zm0 8h18v2H1v-2zM1 5h18v2H1V5zm0 8h18v2H1v-2z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-align-left">
<path d="M1 1h18v2H1V1zm0 8h18v2H1V9zm0 8h18v2H1v-2zM1 5h12v2H1V5zm0 8h12v2H1v-2z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-align-right">
<path d="M1 1h18v2H1V1zm0 8h18v2H1V9zm0 8h18v2H1v-2zM7 5h12v2H7V5zm0 8h12v2H7v-2z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-anchor">
<path d="M4.34 15.66A7.97 7.97 0 0 0 9 17.94V10H5V8h4V5.83a3 3 0 1 1 2 0V8h4v2h-4v7.94a7.97 7.97 0 0 0 4.66-2.28l-1.42-1.42h5.66l-2.83 2.83a10 10 0 0 1-14.14 0L.1 14.24h5.66l-1.42 1.42zM10 4a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-announcement">
<path d="M3 6c0-1.1.9-2 2-2h8l4-4h2v16h-2l-4-4H5a2 2 0 0 1-2-2H1V6h2zm8 9v5H8l-1.67-5H5v-2h8v2h-2z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-apparel">
<path d="M7 0H6L0 3v6l4-1v12h12V8l4 1V3l-6-3h-1a3 3 0 0 1-6 0z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-arrow-outline-down">
<path d="M10 20a10 10 0 1 1 0-20 10 10 0 0 1 0 20zm0-2a8 8 0 1 0 0-16 8 8 0 0 0 0 16zm-2-8V5h4v5h3l-5 5-5-5h3z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-arrow-outline-left">
<path d="M0 10a10 10 0 1 1 20 0 10 10 0 0 1-20 0zm2 0a8 8 0 1 0 16 0 8 8 0 0 0-16 0zm8-2h5v4h-5v3l-5-5 5-5v3z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-arrow-outline-right">
<path d="M20 10a10 10 0 1 1-20 0 10 10 0 0 1 20 0zm-2 0a8 8 0 1 0-16 0 8 8 0 0 0 16 0zm-8 2H5V8h5V5l5 5-5 5v-3z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-arrow-outline-up">
<path d="M10 0a10 10 0 1 1 0 20 10 10 0 0 1 0-20zm0 2a8 8 0 1 0 0 16 8 8 0 0 0 0-16zm2 8v5H8v-5H5l5-5 5 5h-3z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-arrow-thick-down">
<path d="M7 10V2h6v8h5l-8 8-8-8h5z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-arrow-thick-left">
<path d="M10 13h8V7h-8V2l-8 8 8 8v-5z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-arrow-thick-right">
<path d="M10 7H2v6h8v5l8-8-8-8v5z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-arrow-thick-up">
<path d="M7 10v8h6v-8h5l-8-8-8 8h5z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-arrow-thin-down">
<path d="M9 16.172l-6.071-6.071-1.414 1.414L10 20l.707-.707 7.778-7.778-1.414-1.414L11 16.172V0H9z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-arrow-thin-left">
<path d="M3.828 9l6.071-6.071-1.414-1.414L0 10l.707.707 7.778 7.778 1.414-1.414L3.828 11H20V9H3.828z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-arrow-thin-right">
<path d="M16.172 9l-6.071-6.071 1.414-1.414L20 10l-.707.707-7.778 7.778-1.414-1.414L16.172 11H0V9z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-arrow-thin-up">
<path d="M9 3.828L2.929 9.899 1.515 8.485 10 0l.707.707 7.778 7.778-1.414 1.414L11 3.828V20H9V3.828z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-at-symbol">
<path d="M13.6 13.47A4.99 4.99 0 0 1 5 10a5 5 0 0 1 8-4V5h2v6.5a1.5 1.5 0 0 0 3 0V10a8 8 0 1 0-4.42 7.16l.9 1.79A10 10 0 1 1 20 10h-.18.17v1.5a3.5 3.5 0 0 1-6.4 1.97zM10 13a3 3 0 1 0 0-6 3 3 0 0 0 0 6z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-attachment">
<path d="M15 3H7a7 7 0 1 0 0 14h8v-2H7A5 5 0 0 1 7 5h8a3 3 0 0 1 0 6H7a1 1 0 0 1 0-2h8V7H7a3 3 0 1 0 0 6h8a5 5 0 0 0 0-10z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-backspace">
<path d="M0 10l7-7h13v14H7l-7-7zm14.41 0l2.13-2.12-1.42-1.42L13 8.6l-2.12-2.13-1.42 1.42L11.6 10l-2.13 2.12 1.42 1.42L13 11.4l2.12 2.13 1.42-1.42L14.4 10z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-badge">
<path d="M10 12a6 6 0 1 1 0-12 6 6 0 0 1 0 12zm0-3a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm4 2.75V20l-4-4-4 4v-8.25a6.97 6.97 0 0 0 8 0z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-battery-full">
<path d="M0 6c0-1.1.9-2 2-2h16a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V6zm2 0v8h16V6H2zm1 1h4v6H3V7zm5 0h4v6H8V7zm5 0h4v6h-4V7z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-battery-half">
<path d="M0 6c0-1.1.9-2 2-2h16a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V6zm2 0v8h16V6H2zm1 1h4v6H3V7zm5 0h4v6H8V7z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-battery-low">
<path d="M0 6c0-1.1.9-2 2-2h16a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V6zm2 0v8h16V6H2zm1 1h4v6H3V7z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-beverage">
<path d="M9 18v-7L0 2V0h20v2l-9 9v7l5 1v1H4v-1l5-1zm2-10a2 2 0 1 0 0-4 2 2 0 0 0 0 4z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-block">
<path d="M0 10a10 10 0 1 1 20 0 10 10 0 0 1-20 0zm16.32-4.9L5.09 16.31A8 8 0 0 0 16.32 5.09zm-1.41-1.42A8 8 0 0 0 3.68 14.91L14.91 3.68z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-bluetooth">
<path d="M9.41 0l6 6-4 4 4 4-6 6H9v-7.59l-3.3 3.3-1.4-1.42L8.58 10l-4.3-4.3L5.7 4.3 9 7.58V0h.41zM11 4.41V7.6L12.59 6 11 4.41zM12.59 14L11 12.41v3.18L12.59 14z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-bolt">
<path d="M13 8V0L8.11 5.87 3 12h4v8L17 8h-4z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-book-reference">
<path d="M6 4H5a1 1 0 1 1 0-2h11V1a1 1 0 0 0-1-1H4a2 2 0 0 0-2 2v16c0 1.1.9 2 2 2h12a2 2 0 0 0 2-2V5a1 1 0 0 0-1-1h-7v8l-2-2-2 2V4z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-bookmark">
<path d="M2 2c0-1.1.9-2 2-2h12a2 2 0 0 1 2 2v18l-8-4-8 4V2z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-bookmark-outline">
<path d="M2 2c0-1.1.9-2 2-2h12a2 2 0 0 1 2 2v18l-8-4-8 4V2zm2 0v15l6-3 6 3V2H4z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-bookmark-outline-add">
<path d="M2 2c0-1.1.9-2 2-2h12a2 2 0 0 1 2 2v18l-8-4-8 4V2zm2 0v15l6-3 6 3V2H4zm5 5V5h2v2h2v2h-2v2H9V9H7V7h2z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-border-all">
<path d="M11 11v6h6v-6h-6zm0-2h6V3h-6v6zm-2 2H3v6h6v-6zm0-2V3H3v6h6zm-8 9V1h18v18H1v-1z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-border-bottom">
<path d="M1 1h2v2H1V1zm0 4h2v2H1V5zm0 4h2v2H1V9zm0 4h2v2H1v-2zm0 4h18v2H1v-2zM5 1h2v2H5V1zm0 8h2v2H5V9zm4-8h2v2H9V1zm0 4h2v2H9V5zm0 4h2v2H9V9zm0 4h2v2H9v-2zm4-12h2v2h-2V1zm0 8h2v2h-2V9zm4-8h2v2h-2V1zm0 4h2v2h-2V5zm0 4h2v2h-2V9zm0 4h2v2h-2v-2z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-border-horizontal">
<path d="M1 1h2v2H1V1zm0 4h2v2H1V5zm0 4h18v2H1V9zm0 4h2v2H1v-2zm0 4h2v2H1v-2zM5 1h2v2H5V1zm0 16h2v2H5v-2zM9 1h2v2H9V1zm0 4h2v2H9V5zm0 8h2v2H9v-2zm0 4h2v2H9v-2zm4-16h2v2h-2V1zm0 16h2v2h-2v-2zm4-16h2v2h-2V1zm0 4h2v2h-2V5zm0 8h2v2h-2v-2zm0 4h2v2h-2v-2z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-border-inner">
<path d="M9 9V1h2v8h8v2h-8v8H9v-8H1V9h8zM1 1h2v2H1V1zm0 4h2v2H1V5zm0 8h2v2H1v-2zm0 4h2v2H1v-2zM5 1h2v2H5V1zm0 16h2v2H5v-2zm8-16h2v2h-2V1zm0 16h2v2h-2v-2zm4-16h2v2h-2V1zm0 4h2v2h-2V5zm0 8h2v2h-2v-2zm0 4h2v2h-2v-2z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-border-left">
<path d="M1 1h2v18H1V1zm4 0h2v2H5V1zm0 8h2v2H5V9zm0 8h2v2H5v-2zM9 1h2v2H9V1zm0 4h2v2H9V5zm0 4h2v2H9V9zm0 4h2v2H9v-2zm0 4h2v2H9v-2zm4-16h2v2h-2V1zm0 8h2v2h-2V9zm0 8h2v2h-2v-2zm4-16h2v2h-2V1zm0 4h2v2h-2V5zm0 4h2v2h-2V9zm0 4h2v2h-2v-2zm0 4h2v2h-2v-2z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-border-none">
<path d="M1 1h2v2H1V1zm0 4h2v2H1V5zm0 4h2v2H1V9zm0 4h2v2H1v-2zm0 4h2v2H1v-2zM5 1h2v2H5V1zm0 8h2v2H5V9zm0 8h2v2H5v-2zM9 1h2v2H9V1zm0 4h2v2H9V5zm0 4h2v2H9V9zm0 4h2v2H9v-2zm0 4h2v2H9v-2zm4-16h2v2h-2V1zm0 8h2v2h-2V9zm0 8h2v2h-2v-2zm4-16h2v2h-2V1zm0 4h2v2h-2V5zm0 4h2v2h-2V9zm0 4h2v2h-2v-2zm0 4h2v2h-2v-2z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-border-outer">
<path d="M2 19H1V1h18v18H2zm1-2h14V3H3v14zm10-8h2v2h-2V9zM9 9h2v2H9V9zM5 9h2v2H5V9zm4-4h2v2H9V5zm0 8h2v2H9v-2z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-border-right">
<path d="M5 1h2v2H5V1zm0 8h2v2H5V9zm0 8h2v2H5v-2zM9 1h2v2H9V1zm0 4h2v2H9V5zm0 4h2v2H9V9zm0 4h2v2H9v-2zm0 4h2v2H9v-2zm4-16h2v2h-2V1zm0 8h2v2h-2V9zm0 8h2v2h-2v-2zM1 1h2v2H1V1zm0 4h2v2H1V5zm0 4h2v2H1V9zm0 4h2v2H1v-2zm0 4h2v2H1v-2zM17 1h2v18h-2V1z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-border-top">
<path d="M1 1h18v2H1V1zm0 4h2v2H1V5zm0 4h2v2H1V9zm0 4h2v2H1v-2zm0 4h2v2H1v-2zm4-8h2v2H5V9zm0 8h2v2H5v-2zM9 5h2v2H9V5zm0 4h2v2H9V9zm0 4h2v2H9v-2zm0 4h2v2H9v-2zm4-8h2v2h-2V9zm0 8h2v2h-2v-2zm4-12h2v2h-2V5zm0 4h2v2h-2V9zm0 4h2v2h-2v-2zm0 4h2v2h-2v-2z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-border-vertical">
<path d="M1 1h2v2H1V1zm0 4h2v2H1V5zm0 4h2v2H1V9zm0 4h2v2H1v-2zm0 4h2v2H1v-2zM5 1h2v2H5V1zm0 8h2v2H5V9zm0 8h2v2H5v-2zM9 1h2v18H9V1zm4 0h2v2h-2V1zm0 8h2v2h-2V9zm0 8h2v2h-2v-2zm4-16h2v2h-2V1zm0 4h2v2h-2V5zm0 4h2v2h-2V9zm0 4h2v2h-2v-2zm0 4h2v2h-2v-2z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-box">
<path d="M0 2C0 .9.9 0 2 0h16a2 2 0 0 1 2 2v2H0V2zm1 3h18v13a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V5zm6 2v2h6V7H7z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-brightness-down">
<path d="M10 13a3 3 0 1 1 0-6 3 3 0 0 1 0 6zM9 4a1 1 0 1 1 2 0 1 1 0 1 1-2 0zm4.54 1.05a1 1 0 1 1 1.41 1.41 1 1 0 1 1-1.41-1.41zM16 9a1 1 0 1 1 0 2 1 1 0 1 1 0-2zm-1.05 4.54a1 1 0 1 1-1.41 1.41 1 1 0 1 1 1.41-1.41zM11 16a1 1 0 1 1-2 0 1 1 0 1 1 2 0zm-4.54-1.05a1 1 0 1 1-1.41-1.41 1 1 0 1 1 1.41 1.41zM4 11a1 1 0 1 1 0-2 1 1 0 1 1 0 2zm1.05-4.54a1 1 0 1 1 1.41-1.41 1 1 0 1 1-1.41 1.41z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-brightness-up">
<path d="M10 14a4 4 0 1 1 0-8 4 4 0 0 1 0 8zM9 1a1 1 0 1 1 2 0v2a1 1 0 1 1-2 0V1zm6.65 1.94a1 1 0 1 1 1.41 1.41l-1.4 1.4a1 1 0 1 1-1.41-1.41l1.4-1.4zM18.99 9a1 1 0 1 1 0 2h-1.98a1 1 0 1 1 0-2h1.98zm-1.93 6.65a1 1 0 1 1-1.41 1.41l-1.4-1.4a1 1 0 1 1 1.41-1.41l1.4 1.4zM11 18.99a1 1 0 1 1-2 0v-1.98a1 1 0 1 1 2 0v1.98zm-6.65-1.93a1 1 0 1 1-1.41-1.41l1.4-1.4a1 1 0 1 1 1.41 1.41l-1.4 1.4zM1.01 11a1 1 0 1 1 0-2h1.98a1 1 0 1 1 0 2H1.01zm1.93-6.65a1 1 0 1 1 1.41-1.41l1.4 1.4a1 1 0 1 1-1.41 1.41l-1.4-1.4z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-browser-window">
<path d="M0 3c0-1.1.9-2 2-2h16a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V3zm2 2v12h16V5H2z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-browser-window-new">
<path d="M9 10V8h2v2h2v2h-2v2H9v-2H7v-2h2zM0 3c0-1.1.9-2 2-2h16a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V3zm2 2v12h16V5H2z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-browser-window-open">
<path d="M0 3c0-1.1.9-2 2-2h16a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V3zm2 2v12h16V5H2zm8 3l4 5H6l4-5z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-bug">
<path d="M15.3 14.89l2.77 2.77a1 1 0 0 1 0 1.41 1 1 0 0 1-1.41 0l-2.59-2.58A5.99 5.99 0 0 1 11 18V9.04a1 1 0 0 0-2 0V18a5.98 5.98 0 0 1-3.07-1.51l-2.59 2.58a1 1 0 0 1-1.41 0 1 1 0 0 1 0-1.41l2.77-2.77A5.95 5.95 0 0 1 4.07 13H1a1 1 0 1 1 0-2h3V8.41L.93 5.34a1 1 0 0 1 0-1.41 1 1 0 0 1 1.41 0l2.1 2.1h11.12l2.1-2.1a1 1 0 0 1 1.41 0 1 1 0 0 1 0 1.41L16 8.41V11h3a1 1 0 1 1 0 2h-3.07c-.1.67-.32 1.31-.63 1.89zM15 5H5a5 5 0 1 1 10 0z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-buoy">
<path d="M17.16 6.42a8.03 8.03 0 0 0-3.58-3.58l-1.34 2.69a5.02 5.02 0 0 1 2.23 2.23l2.69-1.34zm0 7.16l-2.69-1.34a5.02 5.02 0 0 1-2.23 2.23l1.34 2.69a8.03 8.03 0 0 0 3.58-3.58zM6.42 2.84a8.03 8.03 0 0 0-3.58 3.58l2.69 1.34a5.02 5.02 0 0 1 2.23-2.23L6.42 2.84zM2.84 13.58a8.03 8.03 0 0 0 3.58 3.58l1.34-2.69a5.02 5.02 0 0 1-2.23-2.23l-2.69 1.34zM10 20a10 10 0 1 1 0-20 10 10 0 0 1 0 20zm0-7a3 3 0 1 0 0-6 3 3 0 0 0 0 6z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-calculator">
<path d="M2 2c0-1.1.9-2 2-2h12a2 2 0 0 1 2 2v16a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V2zm3 1v2h10V3H5zm0 4v2h2V7H5zm4 0v2h2V7H9zm4 0v2h2V7h-2zm-8 4v2h2v-2H5zm4 0v2h2v-2H9zm4 0v6h2v-6h-2zm-8 4v2h2v-2H5zm4 0v2h2v-2H9z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-calendar">
<path d="M1 4c0-1.1.9-2 2-2h14a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V4zm2 2v12h14V6H3zm2-6h2v2H5V0zm8 0h2v2h-2V0zM5 9h2v2H5V9zm0 4h2v2H5v-2zm4-4h2v2H9V9zm0 4h2v2H9v-2zm4-4h2v2h-2V9zm0 4h2v2h-2v-2z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-camera">
<path d="M0 6c0-1.1.9-2 2-2h3l2-2h6l2 2h3a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V6zm10 10a5 5 0 1 0 0-10 5 5 0 0 0 0 10zm0-2a3 3 0 1 1 0-6 3 3 0 0 1 0 6z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-chart">
<path d="M4.13 12H4a2 2 0 1 0 1.8 1.11L7.86 10a2.03 2.03 0 0 0 .65-.07l1.55 1.55a2 2 0 1 0 3.72-.37L15.87 8H16a2 2 0 1 0-1.8-1.11L12.14 10a2.03 2.03 0 0 0-.65.07L9.93 8.52a2 2 0 1 0-3.72.37L4.13 12zM0 4c0-1.1.9-2 2-2h16a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V4z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-chart-bar">
<path d="M1 10h3v10H1V10zM6 0h3v20H6V0zm5 8h3v12h-3V8zm5-4h3v16h-3V4z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-chart-pie">
<path d="M19.95 11A10 10 0 1 1 9 .05V11h10.95zm-.08-2.6H11.6V.13a10 10 0 0 1 8.27 8.27z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-chat-bubble-dots">
<path d="M10 15l-4 4v-4H2a2 2 0 0 1-2-2V3c0-1.1.9-2 2-2h16a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2h-8zM5 7v2h2V7H5zm4 0v2h2V7H9zm4 0v2h2V7h-2z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-checkmark">
<path d="M0 11l2-2 5 5L18 3l2 2L7 18z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-checkmark-outline">
<path d="M2.93 17.07A10 10 0 1 1 17.07 2.93 10 10 0 0 1 2.93 17.07zm12.73-1.41A8 8 0 1 0 4.34 4.34a8 8 0 0 0 11.32 11.32zM6.7 9.29L9 11.6l4.3-4.3 1.4 1.42L9 14.4l-3.7-3.7 1.4-1.42z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-cheveron-down">
<path d="M9.293 12.95l.707.707L15.657 8l-1.414-1.414L10 10.828 5.757 6.586 4.343 8z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-cheveron-left">
<path d="M7.05 9.293L6.343 10 12 15.657l1.414-1.414L9.172 10l4.242-4.243L12 4.343z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-cheveron-outline-down">
<path d="M20 10a10 10 0 1 1-20 0 10 10 0 0 1 20 0zM10 2a8 8 0 1 0 0 16 8 8 0 0 0 0-16zm-.7 10.54L5.75 9l1.41-1.41L10 10.4l2.83-2.82L14.24 9 10 13.24l-.7-.7z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-cheveron-outline-left">
<path d="M10 20a10 10 0 1 1 0-20 10 10 0 0 1 0 20zm8-10a8 8 0 1 0-16 0 8 8 0 0 0 16 0zM7.46 9.3L11 5.75l1.41 1.41L9.6 10l2.82 2.83L11 14.24 6.76 10l.7-.7z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-cheveron-outline-right">
<path d="M10 0a10 10 0 1 1 0 20 10 10 0 0 1 0-20zM2 10a8 8 0 1 0 16 0 8 8 0 0 0-16 0zm10.54.7L9 14.25l-1.41-1.41L10.4 10 7.6 7.17 9 5.76 13.24 10l-.7.7z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-cheveron-outline-up">
<path d="M0 10a10 10 0 1 1 20 0 10 10 0 0 1-20 0zm10 8a8 8 0 1 0 0-16 8 8 0 0 0 0 16zm.7-10.54L14.25 11l-1.41 1.41L10 9.6l-2.83 2.8L5.76 11 10 6.76l.7.7z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-cheveron-right">
<path d="M12.95 10.707l.707-.707L8 4.343 6.586 5.757 10.828 10l-4.242 4.243L8 15.657l4.95-4.95z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-cheveron-up">
<path d="M10.707 7.05L10 6.343 4.343 12l1.414 1.414L10 9.172l4.243 4.242L15.657 12z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-clipboard">
<path d="M7.03 2.6a3 3 0 0 1 5.94 0L15 3v1h1a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V6c0-1.1.9-2 2-2h1V3l2.03-.4zM5 6H4v12h12V6h-1v1H5V6zm5-2a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-close">
<path d="M10 8.586L2.929 1.515 1.515 2.929 8.586 10l-7.071 7.071 1.414 1.414L10 11.414l7.071 7.071 1.414-1.414L11.414 10l7.071-7.071-1.414-1.414L10 8.586z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-close-outline">
<path d="M2.93 17.07A10 10 0 1 1 17.07 2.93 10 10 0 0 1 2.93 17.07zm1.41-1.41A8 8 0 1 0 15.66 4.34 8 8 0 0 0 4.34 15.66zm9.9-8.49L11.41 10l2.83 2.83-1.41 1.41L10 11.41l-2.83 2.83-1.41-1.41L8.59 10 5.76 7.17l1.41-1.41L10 8.59l2.83-2.83 1.41 1.41z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-close-solid">
<path d="M2.93 17.07A10 10 0 1 1 17.07 2.93 10 10 0 0 1 2.93 17.07zM11.4 10l2.83-2.83-1.41-1.41L10 8.59 7.17 5.76 5.76 7.17 8.59 10l-2.83 2.83 1.41 1.41L10 11.41l2.83 2.83 1.41-1.41L11.41 10z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-cloud">
<path d="M16.88 9.1A4 4 0 0 1 16 17H5a5 5 0 0 1-1-9.9V7a3 3 0 0 1 4.52-2.59A4.98 4.98 0 0 1 17 8c0 .38-.04.74-.12 1.1z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-cloud-upload">
<path d="M16.88 9.1A4 4 0 0 1 16 17H5a5 5 0 0 1-1-9.9V7a3 3 0 0 1 4.52-2.59A4.98 4.98 0 0 1 17 8c0 .38-.04.74-.12 1.1zM11 11h3l-4-4-4 4h3v3h2v-3z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-code">
<path d="M.7 9.3l4.8-4.8 1.4 1.42L2.84 10l4.07 4.07-1.41 1.42L0 10l.7-.7zm18.6 1.4l.7-.7-5.49-5.49-1.4 1.42L17.16 10l-4.07 4.07 1.41 1.42 4.78-4.78z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-coffee">
<path d="M4 11H2a2 2 0 0 1-2-2V5c0-1.1.9-2 2-2h2V1h14v10a4 4 0 0 1-4 4H8a4 4 0 0 1-4-4zm0-2V5H2v4h2zm-2 8v-1h18v1l-4 2H6l-4-2z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-cog">
<path d="M3.94 6.5L2.22 3.64l1.42-1.42L6.5 3.94c.52-.3 1.1-.54 1.7-.7L9 0h2l.8 3.24c.6.16 1.18.4 1.7.7l2.86-1.72 1.42 1.42-1.72 2.86c.3.52.54 1.1.7 1.7L20 9v2l-3.24.8c-.16.6-.4 1.18-.7 1.7l1.72 2.86-1.42 1.42-2.86-1.72c-.52.3-1.1.54-1.7.7L11 20H9l-.8-3.24c-.6-.16-1.18-.4-1.7-.7l-2.86 1.72-1.42-1.42 1.72-2.86c-.3-.52-.54-1.1-.7-1.7L0 11V9l3.24-.8c.16-.6.4-1.18.7-1.7zM10 13a3 3 0 1 0 0-6 3 3 0 0 0 0 6z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-color-palette">
<path d="M9 20v-1.7l.01-.24L15.07 12h2.94c1.1 0 1.99.89 1.99 2v4a2 2 0 0 1-2 2H9zm0-3.34V5.34l2.08-2.07a1.99 1.99 0 0 1 2.82 0l2.83 2.83a2 2 0 0 1 0 2.82L9 16.66zM0 1.99C0 .9.89 0 2 0h4a2 2 0 0 1 2 2v16a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V2zM4 17a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-compose">
<path d="M2 4v14h14v-6l2-2v10H0V2h10L8 4H2zm10.3-.3l4 4L8 16H4v-4l8.3-8.3zm1.4-1.4L16 0l4 4-2.3 2.3-4-4z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-computer-desktop">
<path d="M7 17H2a2 2 0 0 1-2-2V2C0 .9.9 0 2 0h16a2 2 0 0 1 2 2v13a2 2 0 0 1-2 2h-5l4 2v1H3v-1l4-2zM2 2v11h16V2H2z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-computer-laptop">
<path d="M18 16h2v1a1 1 0 0 1-1 1H1a1 1 0 0 1-1-1v-1h2V4c0-1.1.9-2 2-2h12a2 2 0 0 1 2 2v12zM4 4v9h12V4H4zm4 11v1h4v-1H8z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-conversation">
<path d="M17 11v3l-3-3H8a2 2 0 0 1-2-2V2c0-1.1.9-2 2-2h10a2 2 0 0 1 2 2v7a2 2 0 0 1-2 2h-1zm-3 2v2a2 2 0 0 1-2 2H6l-3 3v-3H2a2 2 0 0 1-2-2V8c0-1.1.9-2 2-2h2v3a4 4 0 0 0 4 4h6z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-credit-card">
<path d="M18 6V4H2v2h16zm0 4H2v6h16v-6zM0 4c0-1.1.9-2 2-2h16a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V4zm4 8h4v2H4v-2z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-currency-dollar">
<path d="M10 20a10 10 0 1 1 0-20 10 10 0 0 1 0 20zm1-5h1a3 3 0 0 0 0-6H7.99a1 1 0 0 1 0-2H14V5h-3V3H9v2H8a3 3 0 1 0 0 6h4a1 1 0 1 1 0 2H6v2h3v2h2v-2z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-dashboard">
<path d="M10 20a10 10 0 1 1 0-20 10 10 0 0 1 0 20zm-5.6-4.29a9.95 9.95 0 0 1 11.2 0 8 8 0 1 0-11.2 0zm6.12-7.64l3.02-3.02 1.41 1.41-3.02 3.02a2 2 0 1 1-1.41-1.41z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-date-add">
<path d="M15 2h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V4c0-1.1.9-2 2-2h2V0h2v2h6V0h2v2zM3 6v12h14V6H3zm6 5V9h2v2h2v2h-2v2H9v-2H7v-2h2z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-dial-pad">
<path d="M5 4a2 2 0 1 1 0-4 2 2 0 0 1 0 4zm5 0a2 2 0 1 1 0-4 2 2 0 0 1 0 4zm5 0a2 2 0 1 1 0-4 2 2 0 0 1 0 4zM5 9a2 2 0 1 1 0-4 2 2 0 0 1 0 4zm5 0a2 2 0 1 1 0-4 2 2 0 0 1 0 4zm5 0a2 2 0 1 1 0-4 2 2 0 0 1 0 4zM5 14a2 2 0 1 1 0-4 2 2 0 0 1 0 4zm5 0a2 2 0 1 1 0-4 2 2 0 0 1 0 4zm0 6a2 2 0 1 1 0-4 2 2 0 0 1 0 4zm5-6a2 2 0 1 1 0-4 2 2 0 0 1 0 4z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-directions">
<path d="M10 0l10 10-10 10L0 10 10 0zM6 10v3h2v-3h3v3l4-4-4-4v3H8a2 2 0 0 0-2 2z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-document">
<path d="M4 18h12V6h-4V2H4v16zm-2 1V0h12l4 4v16H2v-1z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-document-add">
<path d="M9 10V8h2v2h2v2h-2v2H9v-2H7v-2h2zm-5 8h12V6h-4V2H4v16zm-2 1V0h12l4 4v16H2v-1z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-dots-horizontal-double">
<path d="M10 9a2 2 0 1 1 0-4 2 2 0 0 1 0 4zm0 6a2 2 0 1 1 0-4 2 2 0 0 1 0 4z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-dots-horizontal-triple">
<path d="M10 12a2 2 0 1 1 0-4 2 2 0 0 1 0 4zm0-6a2 2 0 1 1 0-4 2 2 0 0 1 0 4zm0 12a2 2 0 1 1 0-4 2 2 0 0 1 0 4z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-download">
<path d="M13 8V2H7v6H2l8 8 8-8h-5zM0 18h20v2H0v-2z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-duplicate">
<path d="M6 6V2c0-1.1.9-2 2-2h10a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2h-4v4a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V8c0-1.1.9-2 2-2h4zm2 0h4a2 2 0 0 1 2 2v4h4V2H8v4zM2 8v10h10V8H2zm4 4v-2h2v2h2v2H8v2H6v-2H4v-2h2z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-edit-copy">
<path d="M6 6V2c0-1.1.9-2 2-2h10a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2h-4v4a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V8c0-1.1.9-2 2-2h4zm2 0h4a2 2 0 0 1 2 2v4h4V2H8v4zM2 8v10h10V8H2z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-edit-crop">
<path d="M14 16H6a2 2 0 0 1-2-2V6H0V4h4V0h2v14h14v2h-4v4h-2v-4zm0-3V6H7V4h7a2 2 0 0 1 2 2v7h-2z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-edit-cut">
<path d="M9.77 11.5l5.34 3.91c.44.33 1.24.59 1.79.59H20L6.89 6.38A3.5 3.5 0 1 0 5.5 8.37L7.73 10 5.5 11.63a3.5 3.5 0 1 0 1.38 1.99l2.9-2.12zM3.5 7a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3zm0 9a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3zM15.1 4.59A3.53 3.53 0 0 1 16.9 4H20l-7.5 5.5L10.45 8l4.65-3.41z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-edit-pencil">
<path d="M12.3 3.7l4 4L4 20H0v-4L12.3 3.7zm1.4-1.4L16 0l4 4-2.3 2.3-4-4z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-education">
<path d="M3.33 8L10 12l10-6-10-6L0 6h10v2H3.33zM0 8v8l2-2.22V9.2L0 8zm10 12l-5-3-2-1.2v-6l7 4.2 7-4.2v6L10 20z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-envelope">
<path d="M18 2a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V4c0-1.1.9-2 2-2h16zm-4.37 9.1L20 16v-2l-5.12-3.9L20 6V4l-10 8L0 4v2l5.12 4.1L0 14v2l6.37-4.9L10 14l3.63-2.9z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-exclamation-outline">
<path d="M2.93 17.07A10 10 0 1 1 17.07 2.93 10 10 0 0 1 2.93 17.07zm12.73-1.41A8 8 0 1 0 4.34 4.34a8 8 0 0 0 11.32 11.32zM9 5h2v6H9V5zm0 8h2v2H9v-2z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-exclamation-solid">
<path d="M2.93 17.07A10 10 0 1 1 17.07 2.93 10 10 0 0 1 2.93 17.07zM9 5v6h2V5H9zm0 8v2h2v-2H9z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-explore">
<path d="M10 20a10 10 0 1 1 0-20 10 10 0 0 1 0 20zM7.88 7.88l-3.54 7.78 7.78-3.54 3.54-7.78-7.78 3.54zM10 11a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-factory">
<path d="M10.5 20H0V7l5 3.33V7l5 3.33V7l5 3.33V0h5v20h-9.5z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-fast-forward">
<path d="M1 5l9 5-9 5V5zm9 0l9 5-9 5V5z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-fast-rewind">
<path d="M19 5v10l-9-5 9-5zm-9 0v10l-9-5 9-5z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-film">
<path d="M0 4c0-1.1.9-2 2-2h16a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V4zm6 0v12h8V4H6zM2 5v2h2V5H2zm0 4v2h2V9H2zm0 4v2h2v-2H2zm14-8v2h2V5h-2zm0 4v2h2V9h-2zm0 4v2h2v-2h-2zM8 7l5 3-5 3V7z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-filter">
<path d="M12 12l8-8V0H0v4l8 8v8l4-4v-4z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-flag">
<path d="M7.667 12H2v8H0V0h12l.333 2H20l-3 6 3 6H8l-.333-2z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-flashlight">
<path d="M13 7v11a2 2 0 0 1-2 2H9a2 2 0 0 1-2-2V7L5 5V3h10v2l-2 2zM9 8v1a1 1 0 1 0 2 0V8a1 1 0 0 0-2 0zM5 0h10v2H5V0z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-folder">
<path d="M0 4c0-1.1.9-2 2-2h7l2 2h7a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V4z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-folder-outline">
<path d="M0 4c0-1.1.9-2 2-2h7l2 2h7a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V4zm2 2v10h16V6H2z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-folder-outline-add">
<path d="M0 4c0-1.1.9-2 2-2h7l2 2h7a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V4zm2 2v10h16V6H2zm7 4V8h2v2h2v2h-2v2H9v-2H7v-2h2z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-format-bold">
<path d="M3 19V1h8a5 5 0 0 1 3.88 8.16A5.5 5.5 0 0 1 11.5 19H3zm7.5-8H7v5h3.5a2.5 2.5 0 1 0 0-5zM7 4v4h3a2 2 0 1 0 0-4H7z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-format-italic">
<path d="M8 1h9v2H8V1zm3 2h3L8 17H5l6-14zM2 17h9v2H2v-2z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-format-text-size">
<path d="M16 9v8h-2V9h-4V7h10v2h-4zM8 5v12H6V5H0V3h15v2H8z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-format-underline">
<path d="M16 9A6 6 0 1 1 4 9V1h3v8a3 3 0 0 0 6 0V1h3v8zM2 17h16v2H2v-2z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-gift">
<path d="M14.83 4H20v6h-1v10H1V10H0V4h5.17A3 3 0 0 1 10 .76 3 3 0 0 1 14.83 4zM8 10H3v8h5v-8zm4 0v8h5v-8h-5zM8 6H2v2h6V6zm4 0v2h6V6h-6zM8 4a1 1 0 1 0 0-2 1 1 0 0 0 0 2zm4 0a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-globe">
<path d="M10 20a10 10 0 1 1 0-20 10 10 0 0 1 0 20zm2-2.25a8 8 0 0 0 4-2.46V9a2 2 0 0 1-2-2V3.07a7.95 7.95 0 0 0-3-1V3a2 2 0 0 1-2 2v1a2 2 0 0 1-2 2v2h3a2 2 0 0 1 2 2v5.75zm-4 0V15a2 2 0 0 1-2-2v-1h-.5A1.5 1.5 0 0 1 4 10.5V8H2.25A8.01 8.01 0 0 0 8 17.75z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-hand-stop">
<path d="M17 16a4 4 0 0 1-4 4H7a4 4 0 0 1-4-4.01V4a1 1 0 0 1 1-1 1 1 0 0 1 1 1v6h1V2a1 1 0 0 1 1-1 1 1 0 0 1 1 1v8h1V1a1 1 0 1 1 2 0v9h1V2a1 1 0 0 1 1-1 1 1 0 0 1 1 1v13h1V9a1 1 0 0 1 1-1h1v8z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-hard-drive">
<path d="M2 2c0-1.1.9-2 2-2h12a2 2 0 0 1 2 2v16a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V2zm10.4 5.6A5 5 0 1 0 15 12V5l-2.6 2.6zM10 14a2 2 0 1 1 0-4 2 2 0 0 1 0 4zM6 3v2h4V3H6zM4 3a1 1 0 1 0 0-2 1 1 0 0 0 0 2zm0 16a1 1 0 1 0 0-2 1 1 0 0 0 0 2zm12 0a1 1 0 1 0 0-2 1 1 0 0 0 0 2zm0-16a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-headphones">
<path d="M16 8A6 6 0 1 0 4 8v11H2a2 2 0 0 1-2-2v-4a2 2 0 0 1 2-2V8a8 8 0 1 1 16 0v3a2 2 0 0 1 2 2v4a2 2 0 0 1-2 2h-2V8zm-4 2h3v10h-3V10zm-7 0h3v10H5V10z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-heart">
<path d="M10 3.22l-.61-.6a5.5 5.5 0 0 0-7.78 7.77L10 18.78l8.39-8.4a5.5 5.5 0 0 0-7.78-7.77l-.61.61z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-home">
<path d="M8 20H3V10H0L10 0l10 10h-3v10h-5v-6H8v6z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-hot">
<path d="M10 0s8 7.58 8 12a8 8 0 1 1-16 0c0-1.5.91-3.35 2.12-5.15A3 3 0 0 0 10 6V0zM8 0a3 3 0 1 0 0 6V0z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-hour-glass">
<path d="M3 18a7 7 0 0 1 4-6.33V8.33A7 7 0 0 1 3 2H1V0h18v2h-2a7 7 0 0 1-4 6.33v3.34A7 7 0 0 1 17 18h2v2H1v-2h2zM5 2a5 5 0 0 0 4 4.9V10h2V6.9A5 5 0 0 0 15 2H5z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-inbox">
<path d="M0 2C0 .9.9 0 2 0h16a2 2 0 0 1 2 2v16a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V2zm14 12h4V2H2v12h4c0 1.1.9 2 2 2h4a2 2 0 0 0 2-2z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-inbox-check">
<path d="M0 2C0 .9.9 0 2 0h16a2 2 0 0 1 2 2v16a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V2zm14 12h4V2H2v12h4c0 1.1.9 2 2 2h4a2 2 0 0 0 2-2zM5 9l2-2 2 2 4-4 2 2-6 6-4-4z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-inbox-download">
<path d="M0 2C0 .9.9 0 2 0h16a2 2 0 0 1 2 2v16a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V2zm14 12h4V2H2v12h4c0 1.1.9 2 2 2h4a2 2 0 0 0 2-2zM9 8V5h2v3h3l-4 4-4-4h3z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-inbox-full">
<path d="M14 14h4V2H2v12h4c0 1.1.9 2 2 2h4a2 2 0 0 0 2-2zM0 2C0 .9.9 0 2 0h16a2 2 0 0 1 2 2v16a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V2zm4 2h12v2H4V4zm0 3h12v2H4V7zm0 3h12v2H4v-2z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-indent-decrease">
<path d="M1 1h18v2H1V1zm6 8h12v2H7V9zm-6 8h18v2H1v-2zM7 5h12v2H7V5zm0 8h12v2H7v-2zM5 6v8l-4-4 4-4z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-indent-increase">
<path d="M1 1h18v2H1V1zm6 8h12v2H7V9zm-6 8h18v2H1v-2zM7 5h12v2H7V5zm0 8h12v2H7v-2zM1 6l4 4-4 4V6z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-information-outline">
<path d="M2.93 17.07A10 10 0 1 1 17.07 2.93 10 10 0 0 1 2.93 17.07zm12.73-1.41A8 8 0 1 0 4.34 4.34a8 8 0 0 0 11.32 11.32zM9 11V9h2v6H9v-4zm0-6h2v2H9V5z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-information-solid">
<path d="M2.93 17.07A10 10 0 1 1 17.07 2.93 10 10 0 0 1 2.93 17.07zM9 11v4h2V9H9v2zm0-6v2h2V5H9z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-key">
<path d="M12.26 11.74L10 14H8v2H6v2l-2 2H0v-4l8.26-8.26a6 6 0 1 1 4 4zm4.86-4.62A3 3 0 0 0 15 2a3 3 0 0 0-2.12.88l4.24 4.24z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-keyboard">
<path d="M0 6c0-1.1.9-2 2-2h16a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V6zm2 0v2h2V6H2zm1 3v2h2V9H3zm-1 3v2h2v-2H2zm3 0v2h10v-2H5zm11 0v2h2v-2h-2zM6 9v2h2V9H6zm3 0v2h2V9H9zm3 0v2h2V9h-2zm3 0v2h2V9h-2zM5 6v2h2V6H5zm3 0v2h2V6H8zm3 0v2h2V6h-2zm3 0v2h4V6h-4z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-layers">
<path d="M10 1l10 6-10 6L0 7l10-6zm6.67 10L20 13l-10 6-10-6 3.33-2L10 15l6.67-4z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-library">
<path d="M0 6l10-6 10 6v2H0V6zm0 12h20v2H0v-2zm2-2h16v2H2v-2zm0-8h4v8H2V8zm6 0h4v8H8V8zm6 0h4v8h-4V8z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-light-bulb">
<path d="M7 13.33a7 7 0 1 1 6 0V16H7v-2.67zM7 17h6v1.5c0 .83-.67 1.5-1.5 1.5h-3A1.5 1.5 0 0 1 7 18.5V17zm2-5.1V14h2v-2.1a5 5 0 1 0-2 0z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-link">
<path d="M9.26 13a2 2 0 0 1 .01-2.01A3 3 0 0 0 9 5H5a3 3 0 0 0 0 6h.08a6.06 6.06 0 0 0 0 2H5A5 5 0 0 1 5 3h4a5 5 0 0 1 .26 10zm1.48-6a2 2 0 0 1-.01 2.01A3 3 0 0 0 11 15h4a3 3 0 0 0 0-6h-.08a6.06 6.06 0 0 0 0-2H15a5 5 0 0 1 0 10h-4a5 5 0 0 1-.26-10z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-list-add">
<path d="M15 9h-3v2h3v3h2v-3h3V9h-3V6h-2v3zM0 3h10v2H0V3zm0 8h10v2H0v-2zm0-4h10v2H0V7zm0 8h10v2H0v-2z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-list-bullet">
<path d="M1 4h2v2H1V4zm4 0h14v2H5V4zM1 9h2v2H1V9zm4 0h14v2H5V9zm-4 5h2v2H1v-2zm4 0h14v2H5v-2z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-load-balancer">
<path d="M17 12h-6v4h1v4H8v-4h1v-4H3v4h1v4H0v-4h1v-4a2 2 0 0 1 2-2h6V6H7V0h6v6h-2v4h6a2 2 0 0 1 2 2v4h1v4h-4v-4h1v-4z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-location">
<path d="M10 20S3 10.87 3 7a7 7 0 1 1 14 0c0 3.87-7 13-7 13zm0-11a2 2 0 1 0 0-4 2 2 0 0 0 0 4z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-location-current">
<path d="M0 0l20 8-8 4-2 8z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-location-food">
<path d="M18 11v7a2 2 0 0 1-4 0v-5h-2V3a3 3 0 0 1 3-3h3v11zM4 10a2 2 0 0 1-2-2V1a1 1 0 0 1 2 0v4h1V1a1 1 0 0 1 2 0v4h1V1a1 1 0 0 1 2 0v7a2 2 0 0 1-2 2v8a2 2 0 0 1-4 0v-8z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-location-gas-station">
<path d="M13 18h1v2H0v-2h1V2c0-1.1.9-2 2-2h8a2 2 0 0 1 2 2v16zM3 2v6h8V2H3zm10 8h1a2 2 0 0 1 2 2v3a1 1 0 0 0 2 0v-5l-2-2V6l-2-2 1-1 5 5v7a3 3 0 0 1-6 0v-3h-1v-2z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-location-hotel">
<path d="M2 12h18v6h-2v-2H2v2H0V2h2v10zm8-6h8a2 2 0 0 1 2 2v3H10V6zm-4 5a3 3 0 1 1 0-6 3 3 0 0 1 0 6z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-location-marina">
<path d="M8 1.88V0h2v16h10l-4 4H2l-2-4h8v-2H0v-.26A24.03 24.03 0 0 0 8 1.88zM19.97 14H10v-.36A11.94 11.94 0 0 0 10 .36v-.2A16.01 16.01 0 0 1 19.97 14z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-location-park">
<path d="M5.33 12.77A4 4 0 1 1 3 5.13V5a4 4 0 0 1 5.71-3.62 3.5 3.5 0 0 1 6.26 1.66 2.5 2.5 0 0 1 2 2.08 4 4 0 1 1-2.7 7.49A5.02 5.02 0 0 1 12 14.58V18l2 1v1H6v-1l2-1v-3l-2.67-2.23zM5 10l3 3v-3H5z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-location-restroom">
<path d="M12 16H9l2-4.5V9c0-1.1.9-2 2-2h2a2 2 0 0 1 2 2v2.5l2 4.5h-3v4h-4v-4zm-5-3h2V9a2 2 0 0 0-2-2H3a2 2 0 0 0-2 2v4h2v7h4v-7zM5 6a3 3 0 1 1 0-6 3 3 0 0 1 0 6zm9 0a3 3 0 1 1 0-6 3 3 0 0 1 0 6z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-location-shopping">
<path d="M16 6v2h2l2 12H0L2 8h2V6a6 6 0 1 1 12 0zm-2 0a4 4 0 1 0-8 0v2h8V6zM4 10v2h2v-2H4zm10 0v2h2v-2h-2z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-lock-closed">
<path d="M4 8V6a6 6 0 1 1 12 0v2h1a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2v-8c0-1.1.9-2 2-2h1zm5 6.73V17h2v-2.27a2 2 0 1 0-2 0zM7 6v2h6V6a3 3 0 0 0-6 0z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-lock-open">
<path d="M4 8V6a6 6 0 1 1 12 0h-3v2h4a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2v-8c0-1.1.9-2 2-2h1zm5 6.73V17h2v-2.27a2 2 0 1 0-2 0zM7 6v2h6V6a3 3 0 0 0-6 0z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-map">
<path d="M0 0l6 4 8-4 6 4v16l-6-4-8 4-6-4V0zm7 6v11l6-3V3L7 6z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-menu">
<path d="M0 3h20v2H0V3zm0 6h20v2H0V9zm0 6h20v2H0v-2z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-mic">
<path d="M9 18v-1.06A8 8 0 0 1 2 9h2a6 6 0 1 0 12 0h2a8 8 0 0 1-7 7.94V18h3v2H6v-2h3zM6 4a4 4 0 1 1 8 0v5a4 4 0 1 1-8 0V4z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-minus-outline">
<path d="M10 20a10 10 0 1 1 0-20 10 10 0 0 1 0 20zm0-2a8 8 0 1 0 0-16 8 8 0 0 0 0 16zm5-9v2H5V9h10z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-minus-solid">
<path d="M10 20a10 10 0 1 1 0-20 10 10 0 0 1 0 20zm5-11H5v2h10V9z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-mobile-devices">
<path d="M17 6V5h-2V2H3v14h5v4h3.25H11a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6zm-5.75 14H3a2 2 0 0 1-2-2V2c0-1.1.9-2 2-2h12a2 2 0 0 1 2 2v4a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2h-5.75zM11 8v8h6V8h-6zm3 11a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-mood-happy">
<path d="M10 20a10 10 0 1 1 0-20 10 10 0 0 1 0 20zm0-2a8 8 0 1 0 0-16 8 8 0 0 0 0 16zM6.5 9a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3zm7 0a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3zm2.16 3a6 6 0 0 1-11.32 0h11.32z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-mood-sad">
<path d="M10 20a10 10 0 1 1 0-20 10 10 0 0 1 0 20zm0-2a8 8 0 1 0 0-16 8 8 0 0 0 0 16zM6.5 9a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3zm7 0a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3zm2.16 6H4.34a6 6 0 0 1 11.32 0z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-mouse">
<path d="M4 9V6A6 6 0 0 1 9 .08V9H4zm0 2v3a6 6 0 1 0 12 0v-3H4zm12-2V6a6 6 0 0 0-5-5.92V9h5z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-music-album">
<path d="M0 0h20v20H0V0zm10 18a8 8 0 1 0 0-16 8 8 0 0 0 0 16zm0-5a3 3 0 1 1 0-6 3 3 0 0 1 0 6z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-music-artist">
<path d="M15.75 8l-3.74-3.75a3.99 3.99 0 0 1 6.82-3.08A4 4 0 0 1 15.75 8zm-13.9 7.3l9.2-9.19 2.83 2.83-9.2 9.2-2.82-2.84zm-1.4 2.83l2.11-2.12 1.42 1.42-2.12 2.12-1.42-1.42zM10 15l2-2v7h-2v-5z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-music-notes">
<path d="M20 2.5V0L6 2v12.17A3 3 0 0 0 5 14H3a3 3 0 0 0 0 6h2a3 3 0 0 0 3-3V5.71L18 4.3v7.88a3 3 0 0 0-1-.17h-2a3 3 0 0 0 0 6h2a3 3 0 0 0 3-3V2.5z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-music-playlist">
<path d="M16 17a3 3 0 0 1-3 3h-2a3 3 0 0 1 0-6h2a3 3 0 0 1 1 .17V1l6-1v4l-4 .67V17zM0 3h12v2H0V3zm0 4h12v2H0V7zm0 4h12v2H0v-2zm0 4h6v2H0v-2z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-navigation-more">
<path d="M4 12a2 2 0 1 1 0-4 2 2 0 0 1 0 4zm6 0a2 2 0 1 1 0-4 2 2 0 0 1 0 4zm6 0a2 2 0 1 1 0-4 2 2 0 0 1 0 4z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-network">
<path d="M10 20a10 10 0 1 1 0-20 10 10 0 0 1 0 20zm7.75-8a8.01 8.01 0 0 0 0-4h-3.82a28.81 28.81 0 0 1 0 4h3.82zm-.82 2h-3.22a14.44 14.44 0 0 1-.95 3.51A8.03 8.03 0 0 0 16.93 14zm-8.85-2h3.84a24.61 24.61 0 0 0 0-4H8.08a24.61 24.61 0 0 0 0 4zm.25 2c.41 2.4 1.13 4 1.67 4s1.26-1.6 1.67-4H8.33zm-6.08-2h3.82a28.81 28.81 0 0 1 0-4H2.25a8.01 8.01 0 0 0 0 4zm.82 2a8.03 8.03 0 0 0 4.17 3.51c-.42-.96-.74-2.16-.95-3.51H3.07zm13.86-8a8.03 8.03 0 0 0-4.17-3.51c.42.96.74 2.16.95 3.51h3.22zm-8.6 0h3.34c-.41-2.4-1.13-4-1.67-4S8.74 3.6 8.33 6zM3.07 6h3.22c.2-1.35.53-2.55.95-3.51A8.03 8.03 0 0 0 3.07 6z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-news-paper">
<path d="M16 2h4v15a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V0h16v2zm0 2v13a1 1 0 0 0 1 1 1 1 0 0 0 1-1V4h-2zM2 2v15a1 1 0 0 0 1 1h11.17a2.98 2.98 0 0 1-.17-1V2H2zm2 8h8v2H4v-2zm0 4h8v2H4v-2zM4 4h8v4H4V4z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-notifications">
<path d="M4 8a6 6 0 0 1 4.03-5.67 2 2 0 1 1 3.95 0A6 6 0 0 1 16 8v6l3 2v1H1v-1l3-2V8zm8 10a2 2 0 1 1-4 0h4z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-notifications-outline">
<path d="M6 8v7h8V8a4 4 0 1 0-8 0zm2.03-5.67a2 2 0 1 1 3.95 0A6 6 0 0 1 16 8v6l3 2v1H1v-1l3-2V8a6 6 0 0 1 4.03-5.67zM12 18a2 2 0 1 1-4 0h4z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-paste">
<path d="M10.5 20H2a2 2 0 0 1-2-2V6c0-1.1.9-2 2-2h1V3l2.03-.4a3 3 0 0 1 5.94 0L13 3v1h1a2 2 0 0 1 2 2v1h-2V6h-1v1H3V6H2v12h5v2h3.5zM8 4a1 1 0 1 0 0-2 1 1 0 0 0 0 2zm2 4h8a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2h-8a2 2 0 0 1-2-2v-8c0-1.1.9-2 2-2zm0 2v8h8v-8h-8z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-pause">
<path d="M5 4h3v12H5V4zm7 0h3v12h-3V4z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-pause-outline">
<path d="M2.93 17.07A10 10 0 1 1 17.07 2.93 10 10 0 0 1 2.93 17.07zm12.73-1.41A8 8 0 1 0 4.34 4.34a8 8 0 0 0 11.32 11.32zM7 6h2v8H7V6zm4 0h2v8h-2V6z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-pause-solid">
<path d="M2.93 17.07A10 10 0 1 1 17.07 2.93 10 10 0 0 1 2.93 17.07zM7 6v8h2V6H7zm4 0v8h2V6h-2z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-pen-tool">
<path d="M11 9.27V0l6 11-4 6H7l-4-6L9 0v9.27a2 2 0 1 0 2 0zM6 18h8v2H6v-2z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-phone">
<path d="M20 18.35V19a1 1 0 0 1-1 1h-2A17 17 0 0 1 0 3V1a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v4c0 .56-.31 1.31-.7 1.7L3.16 8.84c1.52 3.6 4.4 6.48 8 8l2.12-2.12c.4-.4 1.15-.71 1.7-.71H19a1 1 0 0 1 .99 1v3.35z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-photo">
<path d="M0 4c0-1.1.9-2 2-2h16a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V4zm11 9l-3-3-6 6h16l-5-5-2 2zm4-4a2 2 0 1 0 0-4 2 2 0 0 0 0 4z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-php-elephant">
<path fill-rule="evenodd"
d="M10 12v8A10 10 0 0 1 8.17.17L10 2h5a5 5 0 0 1 5 4.99v9.02A4 4 0 0 1 16 20v-2a2 2 0 1 0 0-4h-4l-2-2zm5.5-3a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-pin">
<path d="M11 12h6v-1l-3-1V2l3-1V0H3v1l3 1v8l-3 1v1h6v7l1 1 1-1v-7z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-play">
<path d="M4 4l12 6-12 6z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-play-outline">
<path d="M2.93 17.07A10 10 0 1 1 17.07 2.93 10 10 0 0 1 2.93 17.07zm12.73-1.41A8 8 0 1 0 4.34 4.34a8 8 0 0 0 11.32 11.32zM7 6l8 4-8 4V6z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-plugin">
<path d="M20 14v4a2 2 0 0 1-2 2h-4v-2a2 2 0 0 0-2-2 2 2 0 0 0-2 2v2H6a2 2 0 0 1-2-2v-4H2a2 2 0 0 1-2-2 2 2 0 0 1 2-2h2V6c0-1.1.9-2 2-2h4V2a2 2 0 0 1 2-2 2 2 0 0 1 2 2v2h4a2 2 0 0 1 2 2v4h-2a2 2 0 0 0-2 2 2 2 0 0 0 2 2h2z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-portfolio">
<path d="M9 12H1v6a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-6h-8v2H9v-2zm0-1H0V5c0-1.1.9-2 2-2h4V2a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v1h4a2 2 0 0 1 2 2v6h-9V9H9v2zm3-8V2H8v1h4z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-printer">
<path d="M4 16H0V6h20v10h-4v4H4v-4zm2-4v6h8v-6H6zM4 0h12v5H4V0zM2 8v2h2V8H2zm4 0v2h2V8H6z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-pylon">
<path d="M17.4 18H20v2H0v-2h2.6L8 0h4l5.4 18zm-3.2-4H5.8l-1.2 4h10.8l-1.2-4zm-2.4-8H8.2L7 10h6l-1.2-4z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-question">
<path d="M10 20a10 10 0 1 1 0-20 10 10 0 0 1 0 20zm2-13c0 .28-.21.8-.42 1L10 9.58c-.57.58-1 1.6-1 2.42v1h2v-1c0-.29.21-.8.42-1L13 9.42c.57-.58 1-1.6 1-2.42a4 4 0 1 0-8 0h2a2 2 0 1 1 4 0zm-3 8v2h2v-2H9z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-queue">
<path d="M0 2h20v4H0V2zm0 8h20v2H0v-2zm0 6h20v2H0v-2z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-radar">
<path d="M12 10a2 2 0 0 1-3.41 1.41A2 2 0 0 1 10 8V0a9.97 9.97 0 0 1 10 10h-8zm7.9 1.41A10 10 0 1 1 8.59.1v2.03a8 8 0 1 0 9.29 9.29h2.02zm-4.07 0a6 6 0 1 1-7.25-7.25v2.1a3.99 3.99 0 0 0-1.4 6.57 4 4 0 0 0 6.56-1.42h2.1z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-radio">
<path d="M20 9v9a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V8c0-1.1.9-2 2-2h13.8L.74 1.97 1.26.03 20 5.06V9zm-5 9a3 3 0 1 0 0-6 3 3 0 0 0 0 6zM2 8v2h16V8H2zm1.5 10a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3zm5 0a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3zm6.5-1a2 2 0 1 1 0-4 2 2 0 0 1 0 4z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-refresh">
<path d="M10 3v2a5 5 0 0 0-3.54 8.54l-1.41 1.41A7 7 0 0 1 10 3zm4.95 2.05A7 7 0 0 1 10 17v-2a5 5 0 0 0 3.54-8.54l1.41-1.41zM10 20l-4-4 4-4v8zm0-12V0l4 4-4 4z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-reload">
<path d="M14.66 15.66A8 8 0 1 1 17 10h-2a6 6 0 1 0-1.76 4.24l1.42 1.42zM12 10h8l-4 4-4-4z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-reply">
<path d="M15 17v-2.99A4 4 0 0 0 11 10H8v5L2 9l6-6v5h3a6 6 0 0 1 6 6v3h-2z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-reply-all">
<path d="M18 17v-2.99A4 4 0 0 0 14 10h-3v5L5 9l6-6v5h3a6 6 0 0 1 6 6v3h-2zM6 6V3L0 9l6 6v-3L3 9l3-3z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-repost">
<path d="M5 4a2 2 0 0 0-2 2v6H0l4 4 4-4H5V6h7l2-2H5zm10 4h-3l4-4 4 4h-3v6a2 2 0 0 1-2 2H6l2-2h7V8z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-save-disk">
<path d="M0 2C0 .9.9 0 2 0h14l4 4v14a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V2zm5 0v6h10V2H5zm6 1h3v4h-3V3z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-screen-full">
<path d="M2.8 15.8L0 13v7h7l-2.8-2.8 4.34-4.32-1.42-1.42L2.8 15.8zM17.2 4.2L20 7V0h-7l2.8 2.8-4.34 4.32 1.42 1.42L17.2 4.2zm-1.4 13L13 20h7v-7l-2.8 2.8-4.32-4.34-1.42 1.42 4.33 4.33zM4.2 2.8L7 0H0v7l2.8-2.8 4.32 4.34 1.42-1.42L4.2 2.8z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-search">
<path d="M12.9 14.32a8 8 0 1 1 1.41-1.41l5.35 5.33-1.42 1.42-5.33-5.34zM8 14A6 6 0 1 0 8 2a6 6 0 0 0 0 12z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-send">
<path d="M0 0l20 10L0 20V0zm0 8v4l10-2L0 8z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-servers">
<path d="M0 2C0 .9.9 0 2 0h16a2 2 0 0 1 2 2v2a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V2zm0 7c0-1.1.9-2 2-2h16a2 2 0 0 1 2 2v2a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V9zm0 7c0-1.1.9-2 2-2h16a2 2 0 0 1 2 2v2a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2v-2zM12 2v2h2V2h-2zm4 0v2h2V2h-2zm-4 7v2h2V9h-2zm4 0v2h2V9h-2zm-4 7v2h2v-2h-2zm4 0v2h2v-2h-2z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-share">
<path d="M4 10c0-1.1.9-2 2-2h8c1.1 0 2 .9 2 2v8c0 1.1-.9 2-2 2H6c-1.1 0-2-.9-2-2v-8zm2 0v8h8v-8h-2V8H8v2H6zm3-6.17V16h2V3.83l3.07 3.07 1.42-1.41L10 0l-.7.7-4.8 4.8 1.42 1.4L9 3.84z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-share-alt">
<path d="M5.08 12.16A2.99 2.99 0 0 1 0 10a3 3 0 0 1 5.08-2.16l8.94-4.47a3 3 0 1 1 .9 1.79L5.98 9.63a3.03 3.03 0 0 1 0 .74l8.94 4.47A2.99 2.99 0 0 1 20 17a3 3 0 1 1-5.98-.37l-8.94-4.47z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-shield">
<path d="M19 11a7.5 7.5 0 0 1-3.5 5.94L10 20l-5.5-3.06A7.5 7.5 0 0 1 1 11V3c3.38 0 6.5-1.12 9-3 2.5 1.89 5.62 3 9 3v8zm-9 1.08l2.92 2.04-1.03-3.41 2.84-2.15-3.56-.08L10 5.12 8.83 8.48l-3.56.08L8.1 10.7l-1.03 3.4L10 12.09z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-shopping-cart">
<path d="M4 2h16l-3 9H4a1 1 0 1 0 0 2h13v2H4a3 3 0 0 1 0-6h.33L3 5 2 2H0V0h3a1 1 0 0 1 1 1v1zm1 18a2 2 0 1 1 0-4 2 2 0 0 1 0 4zm10 0a2 2 0 1 1 0-4 2 2 0 0 1 0 4z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-show-sidebar">
<path d="M7 3H2v14h5V3zm2 0v14h9V3H9zM0 3c0-1.1.9-2 2-2h16a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V3zm3 1h3v2H3V4zm0 3h3v2H3V7zm0 3h3v2H3v-2z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-shuffle">
<path d="M6.59 12.83L4.4 15c-.58.58-1.59 1-2.4 1H0v-2h2c.29 0 .8-.2 1-.41l2.17-2.18 1.42 1.42zM16 4V1l4 4-4 4V6h-2c-.29 0-.8.2-1 .41l-2.17 2.18L9.4 7.17 11.6 5c.58-.58 1.59-1 2.41-1h2zm0 10v-3l4 4-4 4v-3h-2c-.82 0-1.83-.42-2.41-1l-8.6-8.59C2.8 6.21 2.3 6 2 6H0V4h2c.82 0 1.83.42 2.41 1l8.6 8.59c.2.2.7.41.99.41h2z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-star-full">
<path d="M10 15l-5.878 3.09 1.123-6.545L.489 6.91l6.572-.955L10 0l2.939 5.955 6.572.955-4.756 4.635 1.123 6.545z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-station">
<path d="M9 11.73a2 2 0 1 1 2 0V20H9v-8.27zm5.24 2.51l-1.41-1.41A3.99 3.99 0 0 0 10 6a4 4 0 0 0-2.83 6.83l-1.41 1.41a6 6 0 1 1 8.49 0zm2.83 2.83l-1.41-1.41a8 8 0 1 0-11.31 0l-1.42 1.41a10 10 0 1 1 14.14 0z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-step-backward">
<path d="M4 5h3v10H4V5zm12 0v10l-9-5 9-5z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-step-forward">
<path d="M13 5h3v10h-3V5zM4 5l9 5-9 5V5z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-stethoscope">
<path d="M17 10.27V4.99a1 1 0 0 0-2 0V15a5 5 0 0 1-10 0v-1.08A6 6 0 0 1 0 8V2C0 .9.9 0 2 0h1a1 1 0 0 1 1 1 1 1 0 0 1-1 1H2v6a4 4 0 1 0 8 0V2H9a1 1 0 0 1-1-1 1 1 0 0 1 1-1h1a2 2 0 0 1 2 2v6a6 6 0 0 1-5 5.92V15a3 3 0 0 0 6 0V5a3 3 0 0 1 6 0v5.27a2 2 0 1 1-2 0z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-store-front">
<path d="M18 9.87V20H2V9.87a4.25 4.25 0 0 0 3-.38V14h10V9.5a4.26 4.26 0 0 0 3 .37zM3 0h4l-.67 6.03A3.43 3.43 0 0 1 3 9C1.34 9 .42 7.73.95 6.15L3 0zm5 0h4l.7 6.3c.17 1.5-.91 2.7-2.42 2.7h-.56A2.38 2.38 0 0 1 7.3 6.3L8 0zm5 0h4l2.05 6.15C19.58 7.73 18.65 9 17 9a3.42 3.42 0 0 1-3.33-2.97L13 0z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-stroke-width">
<path d="M0 0h20v5H0V0zm0 7h20v4H0V7zm0 6h20v3H0v-3zm0 5h20v2H0v-2z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-subdirectory-left">
<path d="M18 12v1H8v5l-6-6 6-6v5h8V2h2z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-subdirectory-right">
<path d="M3.5 13H12v5l6-6-6-6v5H4V2H2v11z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-swap">
<path d="M9 6a4 4 0 1 1 8 0v8h3l-4 4-4-4h3V6a2 2 0 0 0-2-2 2 2 0 0 0-2 2v8a4 4 0 1 1-8 0V6H0l4-4 4 4H5v8a2 2 0 0 0 2 2 2 2 0 0 0 2-2V6z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-tablet">
<path d="M2 2c0-1.1.9-2 2-2h12a2 2 0 0 1 2 2v16a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V2zm2 0v14h12V2H4zm6 17a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-tag">
<path d="M0 10V2l2-2h8l10 10-10 10L0 10zm4.5-4a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-target">
<path d="M17.94 11H13V9h4.94A8 8 0 0 0 11 2.06V7H9V2.06A8 8 0 0 0 2.06 9H7v2H2.06A8 8 0 0 0 9 17.94V13h2v4.94A8 8 0 0 0 17.94 11zM10 20a10 10 0 1 1 0-20 10 10 0 0 1 0 20z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-text-box">
<path d="M0 0h6v6H0V0zm2 2v2h2V2H2zm12-2h6v6h-6V0zm2 2v2h2V2h-2zm-2 12h6v6h-6v-6zm2 2v2h2v-2h-2zM0 14h6v6H0v-6zm2 2v2h2v-2H2zM6 2h8v2H6V2zm0 14h8v2H6v-2zM16 6h2v8h-2V6zM2 6h2v8H2V6zm5 1h6v2H7V7zm2 2h2v4H9V9z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-text-decoration">
<path d="M12 5h-2v12H8V3h8v2h-2v12h-2V5zM8 3a4 4 0 1 0 0 8V3z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-thermometer">
<path d="M9 11.17V7h2v4.17a3 3 0 1 1-2 0zm-1-.63a4 4 0 1 0 4 0V4a2 2 0 1 0-4 0v6.53zM6 9.53V4a4 4 0 0 1 8 0v5.53A5.99 5.99 0 0 1 10 20 6 6 0 0 1 6 9.53z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-thumbs-down">
<path d="M11 20a2 2 0 0 1-2-2v-6H2a2 2 0 0 1-2-2V8l2.3-6.12A3.11 3.11 0 0 1 5 0h8a2 2 0 0 1 2 2v8l-3 7v3h-1zm6-10V0h3v10h-3z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-thumbs-up">
<path d="M11 0h1v3l3 7v8a2 2 0 0 1-2 2H5c-1.1 0-2.31-.84-2.7-1.88L0 12v-2a2 2 0 0 1 2-2h7V2a2 2 0 0 1 2-2zm6 10h3v10h-3V10z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-ticket">
<path d="M20 12v5H0v-5a2 2 0 1 0 0-4V3h20v5a2 2 0 1 0 0 4zM3 5v10h14V5H3zm7 7.08l-2.92 2.04L8.1 10.7 5.27 8.56l3.56-.08L10 5.12l1.17 3.36 3.56.08-2.84 2.15 1.03 3.4L10 12.09z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-time">
<path d="M10 20a10 10 0 1 1 0-20 10 10 0 0 1 0 20zm0-2a8 8 0 1 0 0-16 8 8 0 0 0 0 16zm-1-7.59V4h2v5.59l3.95 3.95-1.41 1.41L9 10.41z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-timer">
<path d="M16.32 7.1A8 8 0 1 1 9 4.06V2h2v2.06c1.46.18 2.8.76 3.9 1.62l1.46-1.46 1.42 1.42-1.46 1.45zM10 18a6 6 0 1 0 0-12 6 6 0 0 0 0 12zM7 0h6v2H7V0zm5.12 8.46l1.42 1.42L10 13.4 8.59 12l3.53-3.54z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-translate">
<path d="M7.41 9l2.24 2.24-.83 2L6 10.4l-3.3 3.3-1.4-1.42L4.58 9l-.88-.88c-.53-.53-1-1.3-1.3-2.12h2.2c.15.28.33.53.51.7l.89.9.88-.88C7.48 6.1 8 4.84 8 4H0V2h5V0h2v2h5v2h-2c0 1.37-.74 3.15-1.7 4.12L7.4 9zm3.84 8L10 20H8l5-12h2l5 12h-2l-1.25-3h-5.5zm.83-2h3.84L14 10.4 12.08 15z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-trash">
<path d="M6 2l2-2h4l2 2h4v2H2V2h4zM3 6h14l-1 14H4L3 6zm5 2v10h1V8H8zm3 0v10h1V8h-1z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-travel-bus">
<path d="M13 18H7v1a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1v-1a2 2 0 0 1-2-2V2c0-1.1.9-2 2-2h12a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2v1a1 1 0 0 1-1 1h-1a1 1 0 0 1-1-1v-1zM4 5v6h5V5H4zm7 0v6h5V5h-5zM5 2v1h10V2H5zm.5 14a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3zm9 0a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-travel-car">
<path d="M2 14v-3H1a1 1 0 0 1-1-1 1 1 0 0 1 1-1h1l4-7h8l4 7h1a1 1 0 0 1 1 1 1 1 0 0 1-1 1h-1v6a1 1 0 0 1-1 1h-1a1 1 0 0 1-1-1v-1H5v1a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1v-3zm13.86-5L13 4H7L4.14 9h11.72zM5.5 14a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3zm9 0a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-travel-case">
<path d="M14 5h2v14H4V5h2V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v1zm3 0h1a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2h-1V5zM3 5v14H2a2 2 0 0 1-2-2V7c0-1.1.9-2 2-2h1zm5-1v1h4V4H8z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-travel-taxi-cab">
<path d="M12 3h2l4 7h1a1 1 0 0 1 1 1 1 1 0 0 1-1 1h-1v6a1 1 0 0 1-1 1h-1a1 1 0 0 1-1-1v-1H5v1a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1v-6H1a1 1 0 0 1-1-1 1 1 0 0 1 1-1h1l4-7h2V1h4v2zm3.86 7L13 5H7l-2.86 5h11.72zM5.5 15a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3zm9 0a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-travel-train">
<path d="M12 18H8l-2 2H3l2-2a2 2 0 0 1-2-2V2c0-1.1.9-2 2-2h10a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2l2 2h-3l-2-2zM5 5v6h10V5H5zm1.5 11a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3zm7 0a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3zM8 2v1h4V2H8z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-travel-walk">
<path d="M11 7l1.44 2.16c.31.47 1.01.84 1.57.84H17V8h-3l-1.44-2.16a5.94 5.94 0 0 0-1.4-1.4l-1.32-.88a1.72 1.72 0 0 0-1.7-.04L4 6v5h2V7l2-1-3 14h2l2.35-7.65L11 14v6h2v-8l-2.7-2.7L11 7zm1-3a2 2 0 1 0 0-4 2 2 0 0 0 0 4z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-trophy">
<path d="M15 9a3 3 0 0 0 3-3h2a5 5 0 0 1-5.1 5 5 5 0 0 1-3.9 3.9V17l5 2v1H4v-1l5-2v-2.1A5 5 0 0 1 5.1 11H5a5 5 0 0 1-5-5h2a3 3 0 0 0 3 3V4H2v2H0V2h5V0h10v2h5v4h-2V4h-3v5z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-tuning">
<path d="M17 16v4h-2v-4h-2v-3h6v3h-2zM1 9h6v3H1V9zm6-4h6v3H7V5zM3 0h2v8H3V0zm12 0h2v12h-2V0zM9 0h2v4H9V0zM3 12h2v8H3v-8zm6-4h2v12H9V8z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-upload">
<path d="M13 10v6H7v-6H2l8-8 8 8h-5zM0 18h20v2H0v-2z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-usb">
<path d="M15 8v2h-4V4h2l-3-4-3 4h2v8H5V9.73a2 2 0 1 0-2 0V12a2 2 0 0 0 2 2h4v2.27a2 2 0 1 0 2 0V12h4a2 2 0 0 0 2-2V8h1V4h-4v4h1z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-user">
<path d="M5 5a5 5 0 0 1 10 0v2A5 5 0 0 1 5 7V5zM0 16.68A19.9 19.9 0 0 1 10 14c3.64 0 7.06.97 10 2.68V20H0v-3.32z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-user-add">
<path d="M2 6H0v2h2v2h2V8h2V6H4V4H2v2zm7 0a3 3 0 0 1 6 0v2a3 3 0 0 1-6 0V6zm11 9.14A15.93 15.93 0 0 0 12 13c-2.91 0-5.65.78-8 2.14V18h16v-2.86z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-user-group">
<path d="M7 8a4 4 0 1 1 0-8 4 4 0 0 1 0 8zm0 1c2.15 0 4.2.4 6.1 1.09L12 16h-1.25L10 20H4l-.75-4H2L.9 10.09A17.93 17.93 0 0 1 7 9zm8.31.17c1.32.18 2.59.48 3.8.92L18 16h-1.25L16 20h-3.96l.37-2h1.25l1.65-8.83zM13 0a4 4 0 1 1-1.33 7.76 5.96 5.96 0 0 0 0-7.52C12.1.1 12.53 0 13 0z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-user-solid-circle">
<path d="M10 20a10 10 0 1 1 0-20 10 10 0 0 1 0 20zM7 6v2a3 3 0 1 0 6 0V6a3 3 0 1 0-6 0zm-3.65 8.44a8 8 0 0 0 13.3 0 15.94 15.94 0 0 0-13.3 0z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-user-solid-square">
<path d="M0 2C0 .9.9 0 2 0h16a2 2 0 0 1 2 2v16a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V2zm7 4v2a3 3 0 1 0 6 0V6a3 3 0 1 0-6 0zm11 9.14A15.93 15.93 0 0 0 10 13c-2.91 0-5.65.78-8 2.14V18h16v-2.86z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-vector">
<path d="M12 4h4.27a2 2 0 1 1 0 2h-2.14a9 9 0 0 1 4.84 7.25 2 2 0 1 1-2 .04 7 7 0 0 0-4.97-6V8H8v-.71a7 7 0 0 0-4.96 6 2 2 0 1 1-2-.04A9 9 0 0 1 5.86 6H3.73a2 2 0 1 1 0-2H8V3h4v1z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-video-camera">
<path d="M16 7l4-4v14l-4-4v3a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V4c0-1.1.9-2 2-2h12a2 2 0 0 1 2 2v3zm-8 7a4 4 0 1 0 0-8 4 4 0 0 0 0 8zm0-2a2 2 0 1 1 0-4 2 2 0 0 1 0 4z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-view-carousel">
<path d="M16 16v2H4v-2H0V4h4V2h12v2h4v12h-4zM14 5.5V4H6v12h8V5.5zm2 .5v8h2V6h-2zM4 6H2v8h2V6z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-view-column">
<path d="M12 4H8v12h4V4zm2 0v12h4V4h-4zM6 4H2v12h4V4zM0 2h20v16H0V2z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-view-hide">
<path d="M12.81 4.36l-1.77 1.78a4 4 0 0 0-4.9 4.9l-2.76 2.75C2.06 12.79.96 11.49.2 10a11 11 0 0 1 12.6-5.64zm3.8 1.85c1.33 1 2.43 2.3 3.2 3.79a11 11 0 0 1-12.62 5.64l1.77-1.78a4 4 0 0 0 4.9-4.9l2.76-2.75zm-.25-3.99l1.42 1.42L3.64 17.78l-1.42-1.42L16.36 2.22z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-view-list">
<path d="M0 3h20v2H0V3zm0 4h20v2H0V7zm0 4h20v2H0v-2zm0 4h20v2H0v-2z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-view-show">
<path d="M.2 10a11 11 0 0 1 19.6 0A11 11 0 0 1 .2 10zm9.8 4a4 4 0 1 0 0-8 4 4 0 0 0 0 8zm0-2a2 2 0 1 1 0-4 2 2 0 0 1 0 4z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-view-tile">
<path d="M0 0h9v9H0V0zm2 2v5h5V2H2zm-2 9h9v9H0v-9zm2 2v5h5v-5H2zm9-13h9v9h-9V0zm2 2v5h5V2h-5zm-2 9h9v9h-9v-9zm2 2v5h5v-5h-5z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-volume-down">
<path d="M7 7H3v6h4l5 5V2L7 7zm8.54 6.54l-1.42-1.42a3 3 0 0 0 0-4.24l1.42-1.42a4.98 4.98 0 0 1 0 7.08z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-volume-mute">
<path d="M9 7H5v6h4l5 5V2L9 7z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-volume-off">
<path d="M15 8.59l-2.12-2.13-1.42 1.42L13.6 10l-2.13 2.12 1.42 1.42L15 11.4l2.12 2.13 1.42-1.42L16.4 10l2.13-2.12-1.42-1.42L15 8.6zM4 7H0v6h4l5 5V2L4 7z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-volume-up">
<path d="M5 7H1v6h4l5 5V2L5 7zm11.36 9.36l-1.41-1.41a6.98 6.98 0 0 0 0-9.9l1.41-1.41a8.97 8.97 0 0 1 0 12.72zm-2.82-2.82l-1.42-1.42a3 3 0 0 0 0-4.24l1.42-1.42a4.98 4.98 0 0 1 0 7.08z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-wallet">
<path d="M0 4c0-1.1.9-2 2-2h15a1 1 0 0 1 1 1v1H2v1h17a1 1 0 0 1 1 1v10a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V4zm16.5 9a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-watch">
<path d="M11 9h2v2H9V7h2v2zm-5.82 6.08a6.98 6.98 0 0 1 0-10.16L6 0h8l.82 4.92a6.98 6.98 0 0 1 0 10.16L14 20H6l-.82-4.92zM10 15a5 5 0 1 0 0-10 5 5 0 0 0 0 10z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-wrench">
<path d="M6.47 9.8A5 5 0 0 1 .2 3.22l3.95 3.95 2.82-2.83L3.03.41a5 5 0 0 1 6.4 6.68l10 10-2.83 2.83L6.47 9.8z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-zoom-in">
<path fill-rule="evenodd"
d="M12.9 14.32a8 8 0 1 1 1.41-1.41l5.35 5.33-1.42 1.42-5.33-5.34zM8 14A6 6 0 1 0 8 2a6 6 0 0 0 0 12zM7 7V5h2v2h2v2H9v2H7V9H5V7h2z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="zondicon-zoom-out">
<path fill-rule="evenodd"
d="M12.9 14.32a8 8 0 1 1 1.41-1.41l5.35 5.33-1.42 1.42-5.33-5.34zM8 14A6 6 0 1 0 8 2a6 6 0 0 0 0 12zM5 7h6v2H5V7z"/>
</symbol>
</svg>

After

Width:  |  Height:  |  Size: 60 KiB

View File

@ -0,0 +1,8 @@
{
"/app.js": "/app.js?id=45904d8bd75c65ee5c136a52a5e8ead6",
"/app-dark.css": "/app-dark.css?id=15c72df05e2b1147fa3e4b0670cfb435",
"/app.css": "/app.css?id=4d6a1a7fe095eedc2cb2a4ce822ea8a5",
"/img/favicon.png": "/img/favicon.png?id=1542bfe8a0010dcbee710da13cce367f",
"/img/horizon.svg": "/img/horizon.svg?id=904d5b5185fefb09035384e15bfca765",
"/img/sprite.svg": "/img/sprite.svg?id=afc4952b74895bdef3ab4ebe9adb746f"
}

View File

@ -1,20 +1,46 @@
/* @tailwind base; */
@tailwind base;
@tailwind components;
@tailwind utilities;
body {
@apply bg-neutral-900 text-white font-sans;
}
a, a:visited {
@apply text-neutral-300 hover:text-purple-500;
@apply bg-coolgray-100 text-white font-sans;
}
input, textarea {
@apply border-none p-2 bg-neutral-800 text-white disabled:text-neutral-600 read-only:text-neutral-600 read-only:select-none
@apply border-none p-2 bg-coolgray-200 text-white disabled:text-neutral-600 read-only:text-neutral-600 read-only:select-none outline-none ;
}
select {
@apply border-none p-2 bg-neutral-800 text-white disabled:text-neutral-600 read-only:select-none
@apply border-none p-2 bg-coolgray-200 text-white disabled:text-neutral-600 read-only:select-none outline-none;
}
button {
@apply border-none px-2 p-1 cursor-pointer;
}
.main-menu {
@apply relative float-left;
}
.main-menu:after {
content: '/';
@apply absolute right-0 top-0 text-neutral-400 px-2 pt-[0.3rem];
}
.magic-input {
@apply w-[25rem] rounded shadow outline-none focus:bg-neutral-700 text-white;
}
.magic-items {
@apply absolute top-14 w-[25rem] bg-coolgray-200 border-b-2 border-r-2 border-l-2 border-solid border-coolgray-100 rounded-b;
}
.magic-item {
@apply m-2 py-2 pl-4 cursor-pointer hover:bg-neutral-700 text-neutral-300 hover:text-white;
}
.magic-item-focused {
@apply bg-neutral-700 text-white;
}
h1 {
@apply text-3xl font-bold py-4;
}
h2 {
@apply text-xl font-bold py-4;
}
.box {
@apply flex items-center justify-center text-sm rounded cursor-pointer h-14 bg-coolgray-200 hover:bg-coollabs p-2;
}

View File

@ -1,4 +1,9 @@
// import './bootstrap';
import Alpine from 'alpinejs'
window.Alpine = Alpine
Alpine.start()
import Alpine from "alpinejs";
// import { createApp } from "vue";
// import MagicSearchBar from "./components/MagicSearchBar.vue";
window.Alpine = Alpine;
Alpine.start();
// const app = createApp({});
// app.component('magic-search-bar', MagicSearchBar);
// app.mount('#vue');

View File

@ -1,5 +1,5 @@
<x-layout>
<div>v{{ config('coolify.version') }}</div>
<div>v{{ config('version') }}</div>
<a href="/login">Login</a>
@if ($is_registration_enabled)
<a href="/register">Register</a>

View File

@ -1,5 +1,5 @@
<x-layout>
<div>v{{ config('coolify.version') }}</div>
<div>v{{ config('version') }}</div>
<a href="/login">Login</a>
<a href="/register">Register</a>
<form action="/register" method="POST">

View File

@ -1,3 +1,3 @@
<x-layout>
<livewire:run-command />
<livewire:run-command :servers="$servers" />
</x-layout>

View File

@ -1,5 +1,22 @@
<nav class="flex gap-4 py-2">
<a href="{{ route('project.application.configuration', Route::current()->parameters()) }}">Configuration</a>
<a href="{{ route('project.application.deployments', Route::current()->parameters()) }}">Deployments</a>
<a target="_blank" href="{{ $gitBranchLocation }}">
<x-inputs.button>Open on Git ↗️</x-inputs.button>
</a>
<a
href="{{ route('project.application.configuration', [
'project_uuid' => Route::current()->parameters()['project_uuid'],
'application_uuid' => Route::current()->parameters()['application_uuid'],
'environment_name' => Route::current()->parameters()['environment_name'],
]) }}">
<x-inputs.button>Configuration</x-inputs.button>
</a>
<a
href="{{ route('project.application.deployments', [
'project_uuid' => Route::current()->parameters()['project_uuid'],
'application_uuid' => Route::current()->parameters()['application_uuid'],
'environment_name' => Route::current()->parameters()['environment_name'],
]) }}">
<x-inputs.button>Deployments</x-inputs.button>
</a>
<livewire:project.application.deploy :applicationId="$applicationId" />
</nav>

View File

@ -1,19 +1,25 @@
@props([
'isWarning' => null,
'defaultClass' => 'text-white bg-neutral-800 hover:bg-violet-600 w-28 h-6',
'defaultWarningClass' => 'text-white bg-red-500 hover:bg-red-600 w-28 h-6',
'loadingClass' => 'text-black bg-green-500 w-28 h-6',
'disabled' => null,
'defaultClass' => 'text-white hover:bg-coollabs h-8 rounded transition-colors',
'defaultWarningClass' => 'text-white bg-red-500 hover:bg-red-600 h-8 rounded',
'disabledClass' => 'text-coolgray-200 h-8 rounded',
'loadingClass' => 'text-black bg-green-500 h-8 rounded',
'confirm' => null,
'confirmAction' => null,
])
<button {{ $attributes }} @class([
$defaultClass => !$confirm && !$isWarning,
$defaultWarningClass => $confirm || $isWarning,
]) @if ($attributes->whereStartsWith('wire:click'))
$defaultClass => !$confirm && !$isWarning && !$disabled,
$defaultWarningClass => ($confirm || $isWarning) && !$disabled,
$disabledClass => $disabled,
]) @if ($attributes->whereStartsWith('wire:click') && !$disabled)
wire:target="{{ explode('(', $attributes->whereStartsWith('wire:click')->first())[0] }}"
wire:loading.delay.class="{{ $loadingClass }}" wire:loading.delay.attr="disabled"
wire:loading.delay.class.remove="{{ $defaultClass }} {{ $attributes->whereStartsWith('class')->first() }}"
@endif
@if ($disabled !== null)
disabled title="{{ $disabled }}"
@endif
@isset($confirm)
x-on:click="toggleConfirmModal('{{ $confirm }}', '{{ explode('(', $confirmAction)[0] }}')"
@endisset

View File

@ -6,15 +6,16 @@
'instantSave' => $attributes->has('instantSave'),
'noLabel' => $attributes->has('noLabel'),
'noDirty' => $attributes->has('noDirty'),
'hidden' => $attributes->has('hidden'),
])
<span @class([
'flex justify-end' => $type === 'checkbox',
'flex' => $type === 'checkbox',
'flex flex-col' => $type !== 'checkbox',
])>
@if (!$noLabel)
<label for={{ $id }} @if (!$noDirty) wire:dirty.class="text-amber-300" @endif
wire:target={{ $id }}>
@if ($hidden) class="hidden" @endif wire:target={{ $id }}>
@if ($label)
{{ $label }}
@else
@ -23,6 +24,7 @@
@if ($required)
*
@endif
</label>
@endif
@if ($type === 'textarea')
@ -31,8 +33,9 @@
@else
<input {{ $attributes }} @if ($required) required @endif
@if (!$noDirty) wire:dirty.class="text-black bg-amber-300" @endif
type={{ $type }} id={{ $id }}
@if ($instantSave) wire:click='instantSave' wire:model.defer={{ $id }} @else wire:model.defer={{ $value ?? $id }} @endif />
type={{ $type }} id={{ $id }} name={{ $id }}
@if ($instantSave) wire:click='instantSave' wire:model.defer={{ $id }} @else wire:model.defer={{ $value ?? $id }} @endif
@if ($hidden) class="hidden" @endif />
@endif
@error($id)
<div class="text-red-500">{{ $message }}</div>

View File

@ -4,6 +4,8 @@
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="preconnect" href="https://api.fonts.coollabs.io" crossorigin>
<link href="https://api.fonts.coollabs.io/css2?family=Inter&display=swap" rel="stylesheet">
@env('local')
<title>Coolify - localhost</title>
@endenv
@ -21,16 +23,28 @@
</head>
<body>
<a
class="fixed text-xs cursor-pointer left-2 bottom-1 opacity-20 hover:opacity-100 hover:text-white">v{{ config('version') }}</a>
@livewireScripts
@auth
<x-navbar />
@endauth
<main>
<main class="max-w-6xl pt-10 mx-auto">
{{ $slot }}
</main>
@livewireScripts
@auth
<script>
window.addEventListener("keydown", function(event) {
if (event.target.nodeName === 'BODY') {
if (event.key === '/') {
event.preventDefault();
window.dispatchEvent(new CustomEvent('slash'));
}
}
})
function checkIfIamDead() {
console.log('Checking server\'s pulse...')
checkIfIamDeadInterval = setInterval(async () => {
@ -76,10 +90,16 @@ function checkHealth() {
window.location.reload();
})
Livewire.on('error', (message) => {
console.log(message);
alert(message);
})
Livewire.on('saved', (message) => {
if (message) console.log(message);
else console.log('saved');
})
</script>
@endauth
</body>
</html>

View File

@ -0,0 +1,541 @@
<div x-data="magicsearchbar" @slash.window="mainMenu = true" class="pt-2">
{{-- Main --}}
<template x-cloak x-if="isMainMenu">
<div>
<div class="main-menu">
<input class="magic-input" x-ref="search" x-model="search" x-on:click="checkMainMenu"
x-on:click.outside="closeMenus" placeholder="Search, jump or create... magically... 🪄"
x-on:keyup.escape="clearSearch" x-on:keydown.down="focusNext(items.length)"
x-on:keydown.up="focusPrev(items.length)"
x-on:keyup.enter="focusedIndex !== '' && await set(filteredItems()[focusedIndex]?.next ?? 'server',filteredItems()[focusedIndex]?.name)" />
</div>
<div x-show="mainMenu" class="magic-items">
<template x-for="(item,index) in filteredItems" :key="item.name">
<div x-on:click="await set(item.next ?? 'server',item.name)"
:class="focusedIndex === index && 'magic-item-focused'" class="magic-item">
<span class="px-2 mr-1 text-xs text-white bg-green-600 rounded" x-show="item.type === 'Add'"
x-text="item.type"></span>
<span class="px-2 mr-1 text-xs text-white bg-purple-600 rounded" x-show="item.type === 'Jump'"
x-text="item.type"></span>
<span class="px-2 mr-1 text-xs text-white bg-blue-600 rounded" x-show="item.type === 'New'"
x-text="item.type"></span>
<span x-text="item.name"></span>
</div>
</template>
</div>
</div>
</template>
{{-- Servers --}}
<template x-cloak x-if="serverMenu">
<div x-on:click.outside="closeMenus">
<input class="magic-input" x-ref="search" x-model="search" placeholder="Select a server..."
x-on:keydown.down="focusNext(servers.length)" x-on:keydown.up="focusPrev(servers.length)"
x-on:keyup.enter="focusedIndex !== '' && await set('destination',filteredServers()[focusedIndex].uuid)" />
<div class="magic-items">
<template x-if="servers.length === 0">
<div class="magic-item" x-on:click="set('newServer')">
<span>No server found. Click here to add a new one!</span>
</div>
</template>
<template x-for="(server,index) in filteredServers" :key="server.name ?? server">
<div x-on:click="await set('destination',server.uuid)"
:class="focusedIndex === index && 'magic-item-focused'" class="magic-item">
<span class="px-2 mr-1 text-xs text-white bg-purple-600 rounded">Server</span>
<span x-text="server.name"></span>
</div>
</template>
</div>
</div>
</template>
{{-- Destinations --}}
<template x-cloak x-if="destinationMenu">
<div x-on:click.outside="closeMenus">
<input class="magic-input" x-ref="search" x-model="search" placeholder="Select a destination..."
x-on:keydown.down="focusNext(destinations.length)" x-on:keydown.up="focusPrev(destinations.length)"
x-on:keyup.escape="closeMenus"
x-on:keyup.enter="focusedIndex !== '' && await set('project',filteredDestinations()[focusedIndex].uuid)" />
<div class="magic-items">
<template x-if="destinations.length === 0">
<div class="magic-item" x-on:click="set('newDestination')">
<span>No destination found. Click here to add a new one!</span>
</div>
</template>
<template x-for="(destination,index) in filteredDestinations" :key="destination.name ?? destination">
<div x-on:click="await set('project',destination.uuid)"
:class="focusedIndex === index && 'magic-item-focused'" class="magic-item">
<span class="px-2 mr-1 text-xs text-white bg-purple-700 rounded">Destination</span>
<span x-text="destination.name"></span>
</div>
</template>
</div>
</div>
</template>
{{-- Project --}}
<template x-cloak x-if="projectMenu">
<div x-on:click.outside="closeMenus">
<input class="magic-input" x-ref="search" x-model="search" placeholder="Type your project name..."
x-on:keydown.down="focusNext(projects.length + 1)" x-on:keydown.up="focusPrev(projects.length + 1)"
x-on:keyup.escape="closeMenus"
x-on:keyup.enter="focusedIndex !== '' && await set('environment',filteredProjects()[focusedIndex - 1]?.uuid)" />
<div class="magic-items">
<div x-on:click="await newProject" class="magic-item"
:class="focusedIndex === 0 && 'magic-item-focused'">
<span>New Project</span>
<span x-text="search"></span>
</div>
<template x-for="(project,index) in filteredProjects" :key="project.name ?? project">
<div x-on:click="await set('environment',project.uuid)"
:class="focusedIndex === index + 1 && 'magic-item-focused'" class="magic-item">
<span class="px-2 mr-1 text-xs text-white bg-purple-700 rounded">Project</span>
<span x-text="project.name"></span>
</div>
</template>
</div>
</div>
</template>
{{-- Environments --}}
<template x-cloak x-if="environmentMenu">
<div x-on:click.outside="closeMenus">
<input class="magic-input" x-ref="search" x-model="search" placeholder="Select a environment..."
x-on:keydown.down="focusNext(environments.length + 1)"
x-on:keydown.up="focusPrev(environments.length + 1)" x-on:keyup.escape="closeMenus"
x-on:keyup.enter="focusedIndex !== '' && await set('jump',filteredEnvironments()[focusedIndex - 1]?.name)" />
<div class="magic-items">
<div x-on:click="await newEnvironment" class="magic-item"
:class="focusedIndex === 0 && 'magic-item-focused'">
<span>New Environment</span>
<span x-text="search"></span>
</div>
<template x-for="(environment,index) in filteredEnvironments" :key="environment.name ?? environment">
<div x-on:click="await set('jump',environment.name)"
:class="focusedIndex === index + 1 && 'magic-item-focused'" class="magic-item">
<span class="px-2 mr-1 text-xs text-white bg-purple-700 rounded">Env</span>
<span x-text="environment.name"></span>
</div>
</template>
</div>
</div>
</template>
{{-- Projects --}}
<template x-cloak x-if="projectsMenu">
<div x-on:click.outside="closeMenus">
<input x-ref="search" x-model="search" class="magic-input" placeholder="Select a project..."
x-on:keyup.escape="closeMenus" x-on:keydown.down="focusNext(projects.length)"
x-on:keydown.up="focusPrev(projects.length)"
x-on:keyup.enter="focusedIndex !== '' && await set('jumpToProject',filteredProjects()[focusedIndex]?.uuid)" />
<div class="magic-items">
<template x-if="projects.length === 0">
<div class="magic-item hover:bg-neutral-800">
<span>No projects found.</span>
</div>
</template>
<template x-for="(project,index) in filteredProjects" :key="project.name ?? project">
<div x-on:click="await set('jumpToProject',project.uuid)"
:class="focusedIndex === index && 'magic-item-focused'"
class="py-2 pl-4 cursor-pointer hover:bg-neutral-700">
<span class="px-2 mr-1 text-xs text-white bg-purple-700 rounded">Jump</span>
<span x-text="project.name"></span>
</div>
</template>
</div>
</div>
</template>
{{-- Destinations --}}
<template x-cloak x-if="destinationsMenu">
<div x-on:click.outside="closeMenus">
<input x-ref="search" x-model="search" class="magic-input" placeholder="Select a destination..."
x-on:keyup.escape="closeMenus" x-on:keydown.down="focusNext(destinations.length)"
x-on:keydown.up="focusPrev(destinations.length)"
x-on:keyup.enter="focusedIndex !== '' && await set('jumpToDestination',filteredDestinations()[focusedIndex].uuid)" />
<div class="magic-items">
<template x-if="destinations.length === 0">
<div class="magic-item" x-on:click="set('newDestination')">
<span>No destination found. Click here to add a new one!</span>
</div>
</template>
<template x-for="(destination,index) in filteredDestinations" :key="destination.name ?? destination">
<div x-on:click="await set('jumpToDestination',destination.uuid)"
:class="focusedIndex === index && 'magic-item-focused'"
class="py-2 pl-4 cursor-pointer hover:bg-neutral-700">
<span class="px-2 mr-1 text-xs bg-purple-700 rounded">Jump</span>
<span x-text="destination.name"></span>
</div>
</template>
</div>
</div>
</template>
</div>
<script>
document.addEventListener('alpine:init', () => {
Alpine.data('magicsearchbar', () => ({
isMainMenu() {
return !this.serverMenu &&
!this.destinationMenu &&
!this.projectMenu &&
!this.environmentMenu &&
!this.projectsMenu &&
!this.destinationsMenu
},
focus() {
if (this.$refs.search) this.$refs.search.focus()
},
init() {
this.$watch('search', () => {
this.focusedIndex = ""
})
this.$watch('mainMenu', () => {
this.focus()
})
this.$watch('serverMenu', () => {
this.focus()
})
this.$watch('destinationMenu', () => {
this.focus()
})
this.$watch('projectMenu', () => {
this.focus()
})
this.$watch('environmentMenu', () => {
this.focus()
})
},
mainMenu: false,
serverMenu: false,
destinationMenu: false,
destinationsMenu: false,
projectMenu: false,
projectsMenu: false,
environmentMenu: false,
search: '',
selectedAction: '',
selectedServer: '',
selectedDestination: '',
selectedProject: '',
selectedEnvironment: '',
servers: ['Loading...'],
destinations: ['Loading...'],
projects: ['Loading...'],
environments: ['Loading...'],
focusedIndex: "",
items: [{
name: 'Server',
type: 'Add',
tags: 'new,server',
next: 'newServer'
},
{
name: 'Destination',
type: 'Add',
tags: 'new,destination',
next: 'newDestination'
},
{
name: 'Private Key',
type: 'Add',
tags: 'new,private-key,ssh,key',
next: 'newPrivateKey'
},
{
name: 'Source',
type: 'Add',
tags: 'new,source,github,gitlab,bitbucket',
next: 'newSource'
},
{
name: 'Public Repository',
type: 'Add',
tags: 'application,public,repository,github,gitlab,bitbucket,git',
},
{
name: 'Private Repository (with GitHub App)',
type: 'Add',
tags: 'application,private,repository,github,gitlab,bitbucket,git',
},
{
name: 'Private Repository (with Deploy Key)',
type: 'Add',
tags: 'application,private,repository,github,gitlab,bitbucket,git',
},
{
name: 'Database',
type: 'Add',
tags: 'data,database,mysql,postgres,sql,sqlite,redis,mongodb,maria,percona',
},
{
name: 'Servers',
type: 'Jump',
tags: 'servers',
next: 'server'
},
{
name: 'Projects',
type: 'Jump',
tags: 'projects',
next: 'projects'
},
{
name: 'Destinations',
type: 'Jump',
tags: 'destinations',
next: 'destinations'
}
],
focusPrev(maxLength) {
if (this.focusedIndex === "") {
this.focusedIndex = maxLength - 1
} else {
if (this.focusedIndex > 0) {
this.focusedIndex = this.focusedIndex - 1
}
}
},
focusNext(maxLength) {
if (this.focusedIndex === "") {
this.focusedIndex = 0
} else {
if (maxLength > this.focusedIndex + 1) {
this.focusedIndex = this.focusedIndex + 1
}
}
},
clearSearch() {
if (this.search === '') {
this.closeMenus()
this.$nextTick(() => {
if (this.$refs.search) this.$refs.search.blur();
})
return
}
this.search = ''
this.focusedIndex = ''
},
closeMenus() {
if (this.$refs.search) this.$refs.search.blur();
this.search = ''
this.focusedIndex = ''
this.mainMenu = false
this.serverMenu = false
this.destinationMenu = false
this.projectMenu = false
this.environmentMenu = false
},
checkMainMenu() {
if (this.serverMenu) return
this.mainMenu = true
},
filteredItems() {
if (this.search === '') return this.items
return this.items.filter(item => {
return item.name.toLowerCase().includes(this.search.toLowerCase()) ||
item.tags.toLowerCase().includes(this.search.toLowerCase())
})
},
filteredServers() {
if (this.search === '') return this.servers
return this.servers.filter(server => {
return server.name.toLowerCase().includes(this.search
.toLowerCase())
})
},
filteredDestinations() {
if (this.search === '') return this.destinations
if (this.destinations.length === 0) return []
return this.destinations.filter(destination => {
return destination.name.toLowerCase().includes(this.search
.toLowerCase())
})
},
filteredProjects() {
if (this.search === '') return this.projects
return this.projects.filter(project => {
return project.name.toLowerCase().includes(this.search
.toLowerCase())
})
},
filteredEnvironments() {
if (this.search === '') return this.environments
return this.environments.filter(environment => {
return environment.name.toLowerCase().includes(this.search
.toLowerCase())
})
},
async newProject() {
const response = await fetch('/magic?server=' + this.selectedServer +
'&destination=' + this.selectedDestination +
'&project=new&name=' + this.search);
if (response.ok) {
const {
project_uuid
} = await response.json();
this.set('environment', project_uuid)
this.set('jump', 'production')
}
},
async newEnvironment() {
const response = await fetch('/magic?server=' + this.selectedServer +
'&destination=' + this.selectedDestination +
'&project=' + this.selectedProject + '&environment=new&name=' + this
.search);
if (response.ok) {
const {
environment_name
} = await response.json();
this.set('jump', environment_name)
}
},
async set(action, id) {
let response = null
switch (action) {
case 'server':
this.items.find((item, index) => {
if (item.name.toLowerCase() === id
.toLowerCase()) {
return this.selectedAction = index
}
})
response = await fetch('/magic?servers=true');
if (response.ok) {
const {
servers
} = await response.json();
this.servers = servers;
}
this.closeMenus()
this.serverMenu = true
break
case 'destination':
this.selectedServer = id
if (this.items[this.selectedAction].type === "Jump") {
return window.location = '/server/' + id
}
response = await fetch('/magic?server=' + this
.selectedServer +
'&destinations=true');
if (response.ok) {
const {
destinations
} = await response.json();
this.destinations = destinations;
}
this.closeMenus()
this.destinationMenu = true
break
case 'project':
this.selectedDestination = id
response = await fetch('/magic?server=' + this
.selectedServer +
'&destination=' + this.selectedDestination +
'&projects=true');
if (response.ok) {
const {
projects
} = await response.json();
this.projects = projects;
}
this.closeMenus()
this.projectMenu = true
break
case 'projects':
response = await fetch('/magic?projects=true');
if (response.ok) {
const {
projects
} = await response.json();
this.projects = projects;
}
this.closeMenus()
this.projectsMenu = true
break
case 'destinations':
response = await fetch('/magic?destinations=true');
if (response.ok) {
const {
destinations
} = await response.json();
this.destinations = destinations;
}
this.closeMenus()
this.destinationsMenu = true
break
case 'environment':
if (this.focusedIndex === 0) {
this.focusedIndex = ''
return await this.newProject()
}
this.selectedProject = id
response = await fetch('/magic?server=' + this
.selectedServer +
'&destination=' + this.selectedDestination +
'&project=' + this
.selectedProject + '&environments=true');
if (response.ok) {
const {
environments
} = await response.json();
this.environments = environments;
}
this.closeMenus()
this.environmentMenu = true
break
case 'jump':
if (this.focusedIndex === 0) {
this.focusedIndex = ''
return await this.newEnvironment()
}
this.selectedEnvironment = id
if (this.selectedAction === 0) {
window.location =
`/project/${this.selectedProject}/${this.selectedEnvironment}/new?type=public&destination=${this.selectedDestination}`
} else if (this.selectedAction === 1) {
window.location =
`/project/${this.selectedProject}/${this.selectedEnvironment}/new?type=private-gh-app&destination=${this.selectedDestination}`
} else if (this.selectedAction === 2) {
window.location =
`/project/${this.selectedProject}/${this.selectedEnvironment}/new?type=private-deploy-key&destination=${this.selectedDestination}`
} else if (this.selectedAction === 3) {
console.log('new Database')
}
this.closeMenus()
break
case 'jumpToProject':
window.location = `/project/${id}`
this.closeMenus()
break
case 'jumpToDestination':
window.location = `/destination/${id}`
this.closeMenus()
break
case 'newServer':
window.location = `/server/new`
this.closeMenus()
break
case 'newDestination':
if (this.selectedServer !== '') {
window.location = `/destination/new?server=${this.selectedServer}`
return
}
window.location = `/destination/new`
this.closeMenus()
break
case 'newPrivateKey':
window.location = `/private-key/new`
this.closeMenus()
break
case 'newSource':
window.location = `/source/new`
this.closeMenus()
break
}
}
}))
})
</script>

View File

@ -1,17 +1,31 @@
<nav class="flex gap-2 ">
<div>v{{ config('coolify.version') }}</div>
<nav class="flex gap-2">
@auth
<a href="/">Home</a>
<a href="/command-center">Command Center</a>
<a href="/profile">Profile</a>
@if (auth()->user()->isRoot())
<a href="/settings">Settings</a>
@endif
<form action="/logout" method="POST">
@csrf
<x-inputs.button type="submit">Logout</x-inputs.button>
</form>
<livewire:check-update />
<livewire:force-upgrade />
<div class="fixed flex gap-2 left-2 top-2">
<a href="/">
<x-inputs.button>Home</x-inputs.button>
</a>
<a href="/command-center">
<x-inputs.button>Command Center</x-inputs.button>
</a>
<a href="/profile">
<x-inputs.button>Profile</x-inputs.button>
</a>
@if (auth()->user()->isRoot())
<a href="/settings">
<x-inputs.button>Settings</x-inputs.button>
</a>
@endif
</div>
<div class="flex-1"></div>
<x-magic-bar />
<div class="flex-1"></div>
<div class="fixed flex gap-2 right-2 top-2">
{{-- <livewire:check-update /> --}}
<livewire:force-upgrade />
<form action="/logout" method="POST">
@csrf
<x-inputs.button type="submit">Logout</x-inputs.button>
</form>
</div>
@endauth
</nav>

View File

@ -1,34 +1,56 @@
<x-layout>
<h1>Projects <a href="{{ route('project.new') }}">
<x-inputs.button>New</x-inputs.button>
</a></h1>
@forelse ($projects as $project)
<a href="{{ route('project.environments', [$project->uuid]) }}">{{ data_get($project, 'name') }}</a>
@empty
<p>No projects found.</p>
@endforelse
<h1>Servers <a href="{{ route('server.new') }}">
<x-inputs.button>New</x-inputs.button>
</a></h1>
@forelse ($servers as $server)
<a href="{{ route('server.show', [$server->uuid]) }}">{{ data_get($server, 'name') }}</a>
@empty
<p>No servers found.</p>
@endforelse
<h1>Destinations <a href="{{ route('destination.new') }}">
<x-inputs.button>New</x-inputs.button>
</a></h1>
@forelse ($destinations as $destination)
<a href="{{ route('destination.show', [$destination->uuid]) }}">{{ data_get($destination, 'name') }}</a>
@empty
<p>No destinations found.</p>
@endforelse
<h1>Private Keys <a href="{{ route('private-key.new') }}">
<x-inputs.button>New</x-inputs.button>
</a></h1>
@forelse ($private_keys as $private_key)
<a href="{{ route('private-key.show', [$private_key->uuid]) }}">{{ data_get($private_key, 'name') }}</a>
@empty
<p>No servers found.</p>
@endforelse
@if ($servers->count() === 0)
<div class="flex flex-col items-center justify-center h-full pt-32">
<div class="pb-3">Without a server, you won't be able to do much.</div>
<div class="text-2xl">Let's create <a href="{{ route('server.new') }}"
class="p-2 rounded bg-coollabs hover:bg-coollabs-100">your
first</a> one!</div>
</div>
@else
<h1>Projects </h1>
<div class="flex gap-2">
@forelse ($projects as $project)
<a href="{{ route('project.environments', [$project->uuid]) }}"
class="box">{{ data_get($project, 'name') }}</a>
@empty
<p>No projects found.</p>
@endforelse
</div>
<h1>Servers </h1>
<div class="flex gap-2">
@forelse ($servers as $server)
<a href="{{ route('server.show', [$server->uuid]) }}" class="box">{{ data_get($server, 'name') }}</a>
@empty
<p>No servers found.</p>
@endforelse
</div>
<h1>Destinations </h1>
<div class="flex gap-2">
@forelse ($destinations as $destination)
<a href="{{ route('destination.show', [$destination->uuid]) }}"
class="box">{{ data_get($destination, 'name') }}</a>
@empty
<p>No destinations found.</p>
@endforelse
</div>
<h1>Private Keys </h1>
<div class="flex gap-2">
@forelse ($private_keys as $private_key)
<a href="{{ route('private-key.show', [$private_key->uuid]) }}"
class="box">{{ data_get($private_key, 'name') }}</a>
@empty
<p>No servers found.</p>
@endforelse
</div>
<h1>GitHub Apps </h1>
<div class="flex">
@forelse ($github_apps as $github_app)
<a href="{{ route('source.github.show', [$github_app->uuid]) }}"
class="box">{{ data_get($github_app, 'name') }}</a>
@empty
<p>No servers found.</p>
@endforelse
</div>
@endif
</x-layout>

View File

@ -1,4 +1,4 @@
<x-layout>
<h1>New Destination</h1>
<livewire:destination.new.standalone-docker :servers="$servers" />
<livewire:destination.new.standalone-docker :servers="$servers" :server_id="$server_id" />
</x-layout>

View File

@ -1,7 +1,10 @@
<div>
@isset($this->activity)
<span>Status: {{ $this->activity?->properties->get('status') }}</span>
<pre class="flex flex-col-reverse w-full overflow-y-scroll"
@if ($isPollingActive) wire:poll.750ms="polling" @endif>{{ \App\Actions\CoolifyTask\RunRemoteProcess::decodeOutput($this->activity) }}</pre>
@endisset
<div
class="flex flex-col-reverse w-full overflow-y-auto border border-solid rounded border-coolgray-300 max-h-[32rem] p-4">
@if ($this->activity)
<pre class="whitespace-pre-wrap" @if ($isPollingActive) wire:poll.750ms="polling" @endif>{{ \App\Actions\CoolifyTask\RunRemoteProcess::decodeOutput($this->activity) }}</pre>
@else
<pre class="whitespace-pre-wrap">Output will be here...</pre>
@endif
</div>
</div>

View File

@ -1,6 +1,6 @@
<div>
<x-inputs.button class="w-32 text-white bg-neutral-800 hover:bg-violet-600" wire:click='checkUpdate' type="submit">
Check for updates</x-inputs.button>
<x-inputs.button wire:click='checkUpdate' type="submit">
Check Update</x-inputs.button>
@if ($updateAvailable)
Update available
@endif

View File

@ -10,7 +10,7 @@
<x-inputs.button>
Submit
</x-inputs.button>
<x-inputs.button isWarning x-on:click="deleteDestination = true">
<x-inputs.button isWarning x-on:click.prevent="deleteDestination = true">
Delete
</x-inputs.button>
</div>

View File

@ -7,7 +7,7 @@
<x-inputs.button type="submit">
Submit
</x-inputs.button>
<x-inputs.button isWarning x-on:click="deletePrivateKey = true">
<x-inputs.button isWarning x-on:click.prevent="deletePrivateKey = true">
Delete
</x-inputs.button>
</form>

View File

@ -1,10 +1,10 @@
<div>
<form class="flex flex-col gap-2 w-96" wire:submit.prevent='createPrivateKey'>
<x-inputs.input id="private_key_name" label="Name" required />
<x-inputs.input id="private_key_description" label="Longer Description" />
<x-inputs.input type="textarea" id="private_key_value" label="Private Key" required />
<x-inputs.button type="submit">
Submit
<form class="flex flex-col gap-2 " wire:submit.prevent='createPrivateKey'>
<x-inputs.input id="name" label="Name" required />
<x-inputs.input id="description" label="Description" />
<x-inputs.input type="textarea" id="value" label="Private Key" required />
<x-inputs.button type="submit" wire.click.prevent>
Save Private Key
</x-inputs.button>
</form>
</div>

View File

@ -8,7 +8,7 @@
<x-inputs.button wire:click='start'>Start</x-inputs.button>
<x-inputs.button wire:click='forceRebuild'>Start (no cache)</x-inputs.button>
@endif
<x-inputs.button isWarning x-on:click="deleteApplication = true">
<x-inputs.button isWarning x-on:click.prevent="deleteApplication = true">
Delete</x-inputs.button>
<span wire:poll.5000ms='pollingStatus'>
@if ($application->status === 'running')

View File

@ -10,7 +10,7 @@
<x-inputs.button type="submit">
Update
</x-inputs.button>
<x-inputs.button x-on:click="deleteEnvironment = true" isWarning>
<x-inputs.button x-on:click.prevent="deleteEnvironment = true" isWarning>
Delete
</x-inputs.button>
</form>

View File

@ -11,12 +11,13 @@
<x-inputs.input id="application.start_command" label="Start Command" />
<x-inputs.select id="application.build_pack" label="Build Pack" required>
<option value="nixpacks">Nixpacks</option>
<option value="docker">Docker</option>
<option disabled value="docker">Docker</option>
<option disabled value="compose">Compose</option>
</x-inputs.select>
@if ($application->settings->is_static)
<x-inputs.select id="application.static_image" label="Static Image" required>
<option value="nginx:alpine">nginx:alpine</option>
<option value="apache:alpine">apache:alpine</option>
<option disabled value="apache:alpine">apache:alpine</option>
</x-inputs.select>
@endif
</div>
@ -42,15 +43,17 @@
Submit
</x-inputs.button>
</form>
<div class="flex flex-col pt-4 text-right w-52">
<x-inputs.input instantSave type="checkbox" id="is_static" label="Static website?" />
<x-inputs.input instantSave type="checkbox" id="is_auto_deploy" label="Auto Deploy?" />
<x-inputs.input instantSave type="checkbox" id="is_dual_cert" label="Dual Certs?" />
<x-inputs.input instantSave type="checkbox" id="is_previews" label="Previews?" />
<x-inputs.input instantSave type="checkbox" id="is_custom_ssl" label="Is Custom SSL?" />
<x-inputs.input instantSave type="checkbox" id="is_http2" label="Is Http2?" />
<x-inputs.input instantSave type="checkbox" id="is_git_submodules_allowed" label="Git Submodules Allowed?" />
<x-inputs.input instantSave type="checkbox" id="is_git_lfs_allowed" label="Git LFS Allowed?" />
<x-inputs.input instantSave type="checkbox" id="is_debug" label="Debug" />
<div class="flex flex-col pt-4">
<x-inputs.input noDirty instantSave type="checkbox" id="is_static" label="Static website?" />
<x-inputs.input noDirty instantSave type="checkbox" id="is_git_submodules_allowed"
label="Git Submodules Allowed?" />
<x-inputs.input noDirty instantSave type="checkbox" id="is_git_lfs_allowed" label="Git LFS Allowed?" />
<x-inputs.input noDirty instantSave type="checkbox" id="is_debug" label="Debug" />
<x-inputs.input noDirty instantSave type="checkbox" id="is_auto_deploy" label="Auto Deploy?" />
<x-inputs.input disabled instantSave type="checkbox" id="is_dual_cert" label="Dual Certs?" />
<x-inputs.input disabled instantSave type="checkbox" id="is_previews" label="Previews?" />
<x-inputs.input disabled instantSave type="checkbox" id="is_custom_ssl" label="Is Custom SSL?" />
<x-inputs.input disabled instantSave type="checkbox" id="is_http2" label="Is Http2?" />
</div>
</div>

View File

@ -1,5 +1,5 @@
<div>
<a @if ($status === 'in_progress' || $status === 'holding') wire:poll='polling' @endif href="{{ url()->current() }}/{{ $deployment_uuid }}">
<a @if ($status === 'in_progress' || $status === 'queued') wire:poll='polling' @endif href="{{ url()->current() }}/{{ $deployment_uuid }}">
{{ $created_at }}
{{ $deployment_uuid }}</a>
{{ $status }}

View File

@ -4,6 +4,12 @@
<div class="flex flex-col w-96">
<x-inputs.input id="application.git_repository" label="Git Repository" readonly />
<x-inputs.input id="application.git_branch" label="Git Branch" readonly />
<x-inputs.input id="application.git_commit_sha" label="Git Commit SHA" readonly />
<form wire:submit.prevent='submit'>
<x-inputs.input id="application.git_commit_sha" placeholder="HEAD" label="Git Commit SHA" />
<x-inputs.button type="submit">Save</x-inputs.button>
</form>
<a target="_blank" href="{{ $application->gitCommits }}">
<x-inputs.button>Commits ↗️</x-inputs.button>
</a>
</div>
</div>

View File

@ -7,7 +7,7 @@
<x-inputs.button type="submit">
Update
</x-inputs.button>
<x-inputs.button x-on:click="deleteStorage = true" isWarning>
<x-inputs.button x-on:click.prevent="deleteStorage = true" isWarning>
Delete
</x-inputs.button>
</form>

Some files were not shown because too many files have changed in this diff Show More