feat: shared environments
This commit is contained in:
parent
abcc004953
commit
fb478c79b3
@ -140,7 +140,7 @@ private function generate_environment_variables()
|
||||
{
|
||||
$environment_variables = collect();
|
||||
foreach ($this->database->runtime_environment_variables as $env) {
|
||||
$environment_variables->push("$env->key=$env->value");
|
||||
$environment_variables->push("$env->key=$env->real_value");
|
||||
}
|
||||
|
||||
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('MARIADB_ROOT_PASSWORD'))->isEmpty()) {
|
||||
|
@ -156,7 +156,7 @@ private function generate_environment_variables()
|
||||
{
|
||||
$environment_variables = collect();
|
||||
foreach ($this->database->runtime_environment_variables as $env) {
|
||||
$environment_variables->push("$env->key=$env->value");
|
||||
$environment_variables->push("$env->key=$env->real_value");
|
||||
}
|
||||
|
||||
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('MONGO_INITDB_ROOT_USERNAME'))->isEmpty()) {
|
||||
|
@ -140,7 +140,7 @@ private function generate_environment_variables()
|
||||
{
|
||||
$environment_variables = collect();
|
||||
foreach ($this->database->runtime_environment_variables as $env) {
|
||||
$environment_variables->push("$env->key=$env->value");
|
||||
$environment_variables->push("$env->key=$env->real_value");
|
||||
}
|
||||
|
||||
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('MYSQL_ROOT_PASSWORD'))->isEmpty()) {
|
||||
|
@ -164,7 +164,7 @@ private function generate_environment_variables()
|
||||
ray('Generate Environment Variables')->green();
|
||||
ray($this->database->runtime_environment_variables)->green();
|
||||
foreach ($this->database->runtime_environment_variables as $env) {
|
||||
$environment_variables->push("$env->key=$env->value");
|
||||
$environment_variables->push("$env->key=$env->real_value");
|
||||
}
|
||||
|
||||
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('POSTGRES_USER'))->isEmpty()) {
|
||||
|
@ -151,7 +151,7 @@ private function generate_environment_variables()
|
||||
{
|
||||
$environment_variables = collect();
|
||||
foreach ($this->database->runtime_environment_variables as $env) {
|
||||
$environment_variables->push("$env->key=$env->value");
|
||||
$environment_variables->push("$env->key=$env->real_value");
|
||||
}
|
||||
|
||||
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('REDIS_PASSWORD'))->isEmpty()) {
|
||||
|
@ -417,11 +417,11 @@ private function save_environment_variables()
|
||||
$envs = collect([]);
|
||||
if ($this->pull_request_id !== 0) {
|
||||
foreach ($this->application->environment_variables_preview as $env) {
|
||||
$envs->push($env->key . '=' . $env->value);
|
||||
$envs->push($env->key . '=' . $env->real_value);
|
||||
}
|
||||
} else {
|
||||
foreach ($this->application->environment_variables as $env) {
|
||||
$envs->push($env->key . '=' . $env->value);
|
||||
$envs->push($env->key . '=' . $env->real_value);
|
||||
}
|
||||
}
|
||||
$envs_base64 = base64_encode($envs->implode("\n"));
|
||||
@ -929,11 +929,11 @@ private function generate_nixpacks_env_variables()
|
||||
$this->env_nixpacks_args = collect([]);
|
||||
if ($this->pull_request_id === 0) {
|
||||
foreach ($this->application->nixpacks_environment_variables as $env) {
|
||||
$this->env_nixpacks_args->push("--env {$env->key}={$env->value}");
|
||||
$this->env_nixpacks_args->push("--env {$env->key}={$env->real_value}");
|
||||
}
|
||||
} else {
|
||||
foreach ($this->application->nixpacks_environment_variables_preview as $env) {
|
||||
$this->env_nixpacks_args->push("--env {$env->key}={$env->value}");
|
||||
$this->env_nixpacks_args->push("--env {$env->key}={$env->real_value}");
|
||||
}
|
||||
}
|
||||
|
||||
@ -944,11 +944,11 @@ private function generate_env_variables()
|
||||
$this->env_args = collect([]);
|
||||
if ($this->pull_request_id === 0) {
|
||||
foreach ($this->application->build_environment_variables as $env) {
|
||||
$this->env_args->put($env->key, $env->value);
|
||||
$this->env_args->put($env->key, $env->real_value);
|
||||
}
|
||||
} else {
|
||||
foreach ($this->application->build_environment_variables_preview as $env) {
|
||||
$this->env_args->put($env->key, $env->value);
|
||||
$this->env_args->put($env->key, $env->real_value);
|
||||
}
|
||||
}
|
||||
$this->env_args->put('SOURCE_COMMIT', $this->commit);
|
||||
@ -1159,22 +1159,19 @@ private function generate_local_persistent_volumes_only_volume_names()
|
||||
private function generate_environment_variables($ports)
|
||||
{
|
||||
$environment_variables = collect();
|
||||
// ray('Generate Environment Variables')->green();
|
||||
if ($this->pull_request_id === 0) {
|
||||
// ray($this->application->runtime_environment_variables)->green();
|
||||
foreach ($this->application->runtime_environment_variables as $env) {
|
||||
$environment_variables->push("$env->key=$env->value");
|
||||
$environment_variables->push("$env->key=$env->real_value");
|
||||
}
|
||||
foreach ($this->application->nixpacks_environment_variables as $env) {
|
||||
$environment_variables->push("$env->key=$env->value");
|
||||
$environment_variables->push("$env->key=$env->real_value");
|
||||
}
|
||||
} else {
|
||||
// ray($this->application->runtime_environment_variables_preview)->green();
|
||||
foreach ($this->application->runtime_environment_variables_preview as $env) {
|
||||
$environment_variables->push("$env->key=$env->value");
|
||||
$environment_variables->push("$env->key=$env->real_value");
|
||||
}
|
||||
foreach ($this->application->nixpacks_environment_variables_preview as $env) {
|
||||
$environment_variables->push("$env->key=$env->value");
|
||||
$environment_variables->push("$env->key=$env->real_value");
|
||||
}
|
||||
}
|
||||
// Add PORT if not exists, use the first port as default
|
||||
@ -1457,12 +1454,12 @@ private function generate_build_env_variables()
|
||||
$this->build_args = collect(["--build-arg SOURCE_COMMIT=\"{$this->commit}\""]);
|
||||
if ($this->pull_request_id === 0) {
|
||||
foreach ($this->application->build_environment_variables as $env) {
|
||||
$value = escapeshellarg($env->value);
|
||||
$value = escapeshellarg($env->real_value);
|
||||
$this->build_args->push("--build-arg {$env->key}={$value}");
|
||||
}
|
||||
} else {
|
||||
foreach ($this->application->build_environment_variables_preview as $env) {
|
||||
$value = escapeshellarg($env->value);
|
||||
$value = escapeshellarg($env->real_value);
|
||||
$this->build_args->push("--build-arg {$env->key}={$value}");
|
||||
}
|
||||
}
|
||||
@ -1478,11 +1475,11 @@ private function add_build_env_variables_to_dockerfile()
|
||||
$dockerfile = collect(Str::of($this->saved_outputs->get('dockerfile'))->trim()->explode("\n"));
|
||||
if ($this->pull_request_id === 0) {
|
||||
foreach ($this->application->build_environment_variables as $env) {
|
||||
$dockerfile->splice(1, 0, "ARG {$env->key}={$env->value}");
|
||||
$dockerfile->splice(1, 0, "ARG {$env->key}={$env->real_value}");
|
||||
}
|
||||
} else {
|
||||
foreach ($this->application->build_environment_variables_preview as $env) {
|
||||
$dockerfile->splice(1, 0, "ARG {$env->key}={$env->value}");
|
||||
$dockerfile->splice(1, 0, "ARG {$env->key}={$env->real_value}");
|
||||
}
|
||||
}
|
||||
$dockerfile_base64 = base64_encode($dockerfile->implode("\n"));
|
||||
|
@ -12,7 +12,24 @@ class Edit extends Component
|
||||
'project.name' => 'required|min:3|max:255',
|
||||
'project.description' => 'nullable|string|max:255',
|
||||
];
|
||||
public function mount() {
|
||||
protected $listeners = ['refreshEnvs' => '$refresh', 'saveKey' => 'saveKey'];
|
||||
|
||||
public function saveKey($data)
|
||||
{
|
||||
try {
|
||||
$this->project->environment_variables()->create([
|
||||
'key' => $data['key'],
|
||||
'value' => $data['value'],
|
||||
'type' => 'project',
|
||||
'team_id' => currentTeam()->id,
|
||||
]);
|
||||
$this->project->refresh();
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
public function mount()
|
||||
{
|
||||
$projectUuid = request()->route('project_uuid');
|
||||
$teamId = currentTeam()->id;
|
||||
$project = Project::where('team_id', $teamId)->where('uuid', $projectUuid)->first();
|
||||
|
@ -12,14 +12,30 @@ class EnvironmentEdit extends Component
|
||||
public Application $application;
|
||||
public $environment;
|
||||
public array $parameters;
|
||||
|
||||
protected $rules = [
|
||||
'environment.name' => 'required|min:3|max:255',
|
||||
'environment.description' => 'nullable|min:3|max:255',
|
||||
];
|
||||
public function mount() {
|
||||
$this->parameters = get_route_parameters();
|
||||
protected $listeners = ['refreshEnvs' => '$refresh', 'saveKey' => 'saveKey'];
|
||||
|
||||
public function saveKey($data)
|
||||
{
|
||||
try {
|
||||
$this->environment->environment_variables()->create([
|
||||
'key' => $data['key'],
|
||||
'value' => $data['value'],
|
||||
'type' => 'environment',
|
||||
'team_id' => currentTeam()->id,
|
||||
]);
|
||||
$this->environment->refresh();
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function mount()
|
||||
{
|
||||
$this->parameters = get_route_parameters();
|
||||
$this->project = Project::ownedByCurrentTeam()->where('uuid', request()->route('project_uuid'))->first();
|
||||
$this->environment = $this->project->environments()->where('name', request()->route('environment_name'))->first();
|
||||
}
|
||||
|
@ -32,7 +32,6 @@ public function mount()
|
||||
public function submit()
|
||||
{
|
||||
$this->validate();
|
||||
ray($this->key, $this->value, $this->is_build_time);
|
||||
$this->dispatch('saveKey', [
|
||||
'key' => $this->key,
|
||||
'value' => $this->value,
|
||||
|
@ -3,16 +3,18 @@
|
||||
namespace App\Livewire\Project\Shared\EnvironmentVariable;
|
||||
|
||||
use App\Models\EnvironmentVariable as ModelsEnvironmentVariable;
|
||||
use App\Models\SharedEnvironmentVariable;
|
||||
use Livewire\Component;
|
||||
use Visus\Cuid2\Cuid2;
|
||||
|
||||
class Show extends Component
|
||||
{
|
||||
public $parameters;
|
||||
public ModelsEnvironmentVariable $env;
|
||||
public ModelsEnvironmentVariable|SharedEnvironmentVariable $env;
|
||||
public ?string $modalId = null;
|
||||
public bool $isDisabled = false;
|
||||
public bool $isLocked = false;
|
||||
public bool $isSharedVariable = false;
|
||||
public string $type;
|
||||
|
||||
protected $rules = [
|
||||
@ -20,16 +22,20 @@ class Show extends Component
|
||||
'env.value' => 'nullable',
|
||||
'env.is_build_time' => 'required|boolean',
|
||||
'env.is_shown_once' => 'required|boolean',
|
||||
'env.real_value' => 'nullable',
|
||||
];
|
||||
protected $validationAttributes = [
|
||||
'key' => 'Key',
|
||||
'value' => 'Value',
|
||||
'is_build_time' => 'Build Time',
|
||||
'is_shown_once' => 'Shown Once',
|
||||
'env.key' => 'Key',
|
||||
'env.value' => 'Value',
|
||||
'env.is_build_time' => 'Build Time',
|
||||
'env.is_shown_once' => 'Shown Once',
|
||||
];
|
||||
|
||||
public function mount()
|
||||
{
|
||||
if ($this->env->getMorphClass() === 'App\Models\SharedEnvironmentVariable') {
|
||||
$this->isSharedVariable = true;
|
||||
}
|
||||
$this->modalId = new Cuid2(7);
|
||||
$this->parameters = get_route_parameters();
|
||||
$this->checkEnvs();
|
||||
@ -44,9 +50,16 @@ public function checkEnvs()
|
||||
$this->isLocked = true;
|
||||
}
|
||||
}
|
||||
public function serialize() {
|
||||
data_forget($this->env, 'real_value');
|
||||
if ($this->env->getMorphClass() === 'App\Models\SharedEnvironmentVariable') {
|
||||
data_forget($this->env, 'is_build_time');
|
||||
}
|
||||
}
|
||||
public function lock()
|
||||
{
|
||||
$this->env->is_shown_once = true;
|
||||
$this->serialize();
|
||||
$this->env->save();
|
||||
$this->checkEnvs();
|
||||
$this->dispatch('refreshEnvs');
|
||||
@ -57,10 +70,23 @@ public function instantSave()
|
||||
}
|
||||
public function submit()
|
||||
{
|
||||
$this->validate();
|
||||
$this->env->save();
|
||||
$this->dispatch('success', 'Environment variable updated successfully.');
|
||||
$this->dispatch('refreshEnvs');
|
||||
try {
|
||||
if ($this->isSharedVariable) {
|
||||
$this->validate([
|
||||
'env.key' => 'required|string',
|
||||
'env.value' => 'nullable',
|
||||
'env.is_shown_once' => 'required|boolean',
|
||||
]);
|
||||
} else {
|
||||
$this->validate();
|
||||
}
|
||||
$this->serialize();
|
||||
$this->env->save();
|
||||
$this->dispatch('success', 'Environment variable updated successfully.');
|
||||
$this->dispatch('refreshEnvs');
|
||||
} catch(\Exception $e) {
|
||||
return handleError($e);
|
||||
}
|
||||
}
|
||||
|
||||
public function delete()
|
||||
|
36
app/Livewire/TeamSharedVariablesIndex.php
Normal file
36
app/Livewire/TeamSharedVariablesIndex.php
Normal file
@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire;
|
||||
|
||||
use App\Models\Team;
|
||||
use Livewire\Component;
|
||||
|
||||
class TeamSharedVariablesIndex extends Component
|
||||
{
|
||||
public Team $team;
|
||||
protected $listeners = ['refreshEnvs' => '$refresh', 'saveKey' => 'saveKey'];
|
||||
|
||||
public function saveKey($data)
|
||||
{
|
||||
try {
|
||||
$this->team->environment_variables()->create([
|
||||
'key' => $data['key'],
|
||||
'value' => $data['value'],
|
||||
'type' => 'team',
|
||||
'team_id' => currentTeam()->id,
|
||||
]);
|
||||
$this->team->refresh();
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function mount()
|
||||
{
|
||||
$this->team = currentTeam();
|
||||
}
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.team-shared-variables-index');
|
||||
}
|
||||
}
|
@ -263,6 +263,10 @@ public function portsExposesArray(): Attribute
|
||||
: explode(',', $this->ports_exposes)
|
||||
);
|
||||
}
|
||||
public function team()
|
||||
{
|
||||
return data_get($this, 'environment.project.team');
|
||||
}
|
||||
public function serviceType()
|
||||
{
|
||||
$found = str(collect(SPECIFIC_SERVICES)->filter(function ($service) {
|
||||
@ -431,7 +435,7 @@ public function isConfigurationChanged($save = false)
|
||||
{
|
||||
$newConfigHash = $this->fqdn . $this->git_repository . $this->git_branch . $this->git_commit_sha . $this->build_pack . $this->static_image . $this->install_command . $this->build_command . $this->start_command . $this->port_exposes . $this->port_mappings . $this->base_directory . $this->publish_directory . $this->dockerfile . $this->dockerfile_location . $this->custom_labels;
|
||||
if ($this->pull_request_id === 0 || $this->pull_request_id === null) {
|
||||
$newConfigHash .= json_encode($this->environment_variables->all());
|
||||
$newConfigHash .= json_encode($this->environment_variables());
|
||||
} else {
|
||||
$newConfigHash .= json_encode($this->environment_variables_preview->all());
|
||||
}
|
||||
|
@ -17,6 +17,9 @@ public function isEmpty()
|
||||
$this->services()->count() == 0;
|
||||
}
|
||||
|
||||
public function environment_variables() {
|
||||
return $this->hasMany(SharedEnvironmentVariable::class);
|
||||
}
|
||||
public function applications()
|
||||
{
|
||||
return $this->hasMany(Application::class);
|
||||
|
@ -15,6 +15,7 @@ class EnvironmentVariable extends Model
|
||||
'value' => 'encrypted',
|
||||
'is_build_time' => 'boolean',
|
||||
];
|
||||
protected $appends = ['real_value', 'is_shared'];
|
||||
|
||||
protected static function booted()
|
||||
{
|
||||
@ -48,24 +49,94 @@ protected function value(): Attribute
|
||||
set: fn (?string $value = null) => $this->set_environment_variables($value),
|
||||
);
|
||||
}
|
||||
|
||||
private function get_environment_variables(?string $environment_variable = null): string|null
|
||||
protected function realValue(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: fn () => $this->get_real_environment_variables($this->value),
|
||||
);
|
||||
}
|
||||
protected function isShared(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: function () {
|
||||
$type = str($this->value)->after("{{")->before(".")->value;
|
||||
if (str($this->value)->startsWith('{{' . $type) && str($this->value)->endsWith('}}')) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
);
|
||||
}
|
||||
private function team()
|
||||
{
|
||||
if ($this->application_id) {
|
||||
$application = Application::find($this->application_id);
|
||||
if ($application) {
|
||||
return $application->team();
|
||||
}
|
||||
}
|
||||
if ($this->service_id) {
|
||||
$service = Service::find($this->service_id);
|
||||
if ($service) {
|
||||
return $service->team();
|
||||
}
|
||||
}
|
||||
if ($this->standalone_postgresql_id) {
|
||||
$standalone_postgresql = StandalonePostgresql::find($this->standalone_postgresql_id);
|
||||
if ($standalone_postgresql) {
|
||||
return $standalone_postgresql->team();
|
||||
}
|
||||
}
|
||||
if ($this->standalone_mysql_id) {
|
||||
$standalone_mysql = StandaloneMysql::find($this->standalone_mysql_id);
|
||||
if ($standalone_mysql) {
|
||||
return $standalone_mysql->team();
|
||||
}
|
||||
}
|
||||
if ($this->standalone_redis_id) {
|
||||
$standalone_redis = StandaloneRedis::find($this->standalone_redis_id);
|
||||
if ($standalone_redis) {
|
||||
return $standalone_redis->team();
|
||||
}
|
||||
}
|
||||
if ($this->standalone_mongodb_id) {
|
||||
$standalone_mongodb = StandaloneMongodb::find($this->standalone_mongodb_id);
|
||||
if ($standalone_mongodb) {
|
||||
return $standalone_mongodb->team();
|
||||
}
|
||||
}
|
||||
if ($this->standalone_mariadb_id) {
|
||||
$standalone_mariadb = StandaloneMariadb::find($this->standalone_mariadb_id);
|
||||
if ($standalone_mariadb) {
|
||||
return $standalone_mariadb->team();
|
||||
}
|
||||
}
|
||||
}
|
||||
private function get_real_environment_variables(?string $environment_variable = null): string|null
|
||||
{
|
||||
// $team_id = currentTeam()->id;
|
||||
if (!$environment_variable) {
|
||||
return null;
|
||||
}
|
||||
$environment_variable = trim(decrypt($environment_variable));
|
||||
if (Str::startsWith($environment_variable, '{{') && Str::endsWith($environment_variable, '}}') && Str::contains($environment_variable, 'global.')) {
|
||||
$variable = Str::after($environment_variable, 'global.');
|
||||
$environment_variable = trim($environment_variable);
|
||||
$type = str($environment_variable)->after("{{")->before(".")->value;
|
||||
if (str($environment_variable)->startsWith("{{" . $type) && str($environment_variable)->endsWith('}}')) {
|
||||
$variable = Str::after($environment_variable, "{$type}.");
|
||||
$variable = Str::before($variable, '}}');
|
||||
$variable = Str::of($variable)->trim()->value;
|
||||
// $environment_variable = GlobalEnvironmentVariable::where('name', $environment_variable)->where('team_id', $team_id)->first()?->value;
|
||||
ray('global env variable');
|
||||
return $environment_variable;
|
||||
$environment_variable_found = SharedEnvironmentVariable::where("type", $type)->where('key', $variable)->where('team_id', $this->team()->id)->first();
|
||||
if ($environment_variable_found) {
|
||||
return $environment_variable_found->value;
|
||||
}
|
||||
}
|
||||
return $environment_variable;
|
||||
}
|
||||
private function get_environment_variables(?string $environment_variable = null): string|null
|
||||
{
|
||||
if (!$environment_variable) {
|
||||
return null;
|
||||
}
|
||||
return trim(decrypt($environment_variable));
|
||||
}
|
||||
|
||||
private function set_environment_variables(?string $environment_variable = null): string|null
|
||||
{
|
||||
@ -73,6 +144,10 @@ private function set_environment_variables(?string $environment_variable = null)
|
||||
return null;
|
||||
}
|
||||
$environment_variable = trim($environment_variable);
|
||||
$type = str($environment_variable)->after("{{")->before(".")->value;
|
||||
if (str($environment_variable)->startsWith("{{" . $type) && str($environment_variable)->endsWith('}}')) {
|
||||
return encrypt((string) str($environment_variable)->replace(' ', ''));
|
||||
}
|
||||
return encrypt($environment_variable);
|
||||
}
|
||||
|
||||
|
@ -27,7 +27,9 @@ protected static function booted()
|
||||
$project->settings()->delete();
|
||||
});
|
||||
}
|
||||
|
||||
public function environment_variables() {
|
||||
return $this->hasMany(SharedEnvironmentVariable::class);
|
||||
}
|
||||
public function environments()
|
||||
{
|
||||
return $this->hasMany(Environment::class);
|
||||
|
@ -8,6 +8,7 @@
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Str;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
class Service extends BaseModel
|
||||
{
|
||||
@ -17,6 +18,10 @@ public function type()
|
||||
{
|
||||
return 'service';
|
||||
}
|
||||
public function team()
|
||||
{
|
||||
return data_get($this, 'environment.project.team');
|
||||
}
|
||||
public function extraFields()
|
||||
{
|
||||
$fields = collect([]);
|
||||
@ -423,7 +428,7 @@ public function saveComposeConfigs()
|
||||
$envs = $this->environment_variables()->get();
|
||||
$commands[] = "rm -f .env || true";
|
||||
foreach ($envs as $env) {
|
||||
$commands[] = "echo '{$env->key}={$env->value}' >> .env";
|
||||
$commands[] = "echo '{$env->key}={$env->real_value}' >> .env";
|
||||
}
|
||||
if ($envs->count() === 0) {
|
||||
$commands[] = "touch .env";
|
||||
|
14
app/Models/SharedEnvironmentVariable.php
Normal file
14
app/Models/SharedEnvironmentVariable.php
Normal file
@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class SharedEnvironmentVariable extends Model
|
||||
{
|
||||
protected $guarded = [];
|
||||
protected $casts = [
|
||||
'key' => 'string',
|
||||
'value' => 'encrypted',
|
||||
];
|
||||
}
|
@ -42,6 +42,10 @@ protected static function booted()
|
||||
$database->environment_variables()->delete();
|
||||
});
|
||||
}
|
||||
public function team()
|
||||
{
|
||||
return data_get($this, 'environment.project.team');
|
||||
}
|
||||
public function link()
|
||||
{
|
||||
if (data_get($this, 'environment.project.uuid')) {
|
||||
|
@ -45,6 +45,10 @@ protected static function booted()
|
||||
$database->environment_variables()->delete();
|
||||
});
|
||||
}
|
||||
public function team()
|
||||
{
|
||||
return data_get($this, 'environment.project.team');
|
||||
}
|
||||
public function isLogDrainEnabled()
|
||||
{
|
||||
return data_get($this, 'is_log_drain_enabled', false);
|
||||
|
@ -42,6 +42,10 @@ protected static function booted()
|
||||
$database->environment_variables()->delete();
|
||||
});
|
||||
}
|
||||
public function team()
|
||||
{
|
||||
return data_get($this, 'environment.project.team');
|
||||
}
|
||||
public function link()
|
||||
{
|
||||
if (data_get($this, 'environment.project.uuid')) {
|
||||
|
@ -74,7 +74,10 @@ public function portsMappingsArray(): Attribute
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
public function team()
|
||||
{
|
||||
return data_get($this, 'environment.project.team');
|
||||
}
|
||||
public function type(): string
|
||||
{
|
||||
return 'standalone-postgresql';
|
||||
|
@ -37,6 +37,10 @@ protected static function booted()
|
||||
$database->environment_variables()->delete();
|
||||
});
|
||||
}
|
||||
public function team()
|
||||
{
|
||||
return data_get($this, 'environment.project.team');
|
||||
}
|
||||
public function link()
|
||||
{
|
||||
if (data_get($this, 'environment.project.uuid')) {
|
||||
|
@ -70,7 +70,9 @@ public function limits(): Attribute
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
public function environment_variables() {
|
||||
return $this->hasMany(SharedEnvironmentVariable::class)->whereNull('project_id')->whereNull('environment_id');
|
||||
}
|
||||
public function members()
|
||||
{
|
||||
return $this->belongsToMany(User::class, 'team_user', 'team_id', 'user_id')->withPivot('role');
|
||||
|
@ -19,7 +19,7 @@ public function __construct(
|
||||
public string|null $label = null,
|
||||
public string|null $helper = null,
|
||||
public bool $required = false,
|
||||
public string $defaultClass = "select select-sm w-full rounded text-white text-sm bg-coolgray-100 font-normal disabled:bg-coolgray-200/50 disabled:border-none"
|
||||
public string $defaultClass = "select select-sm w-full rounded text-sm bg-coolgray-100 font-normal disabled:bg-coolgray-200/50 disabled:border-none"
|
||||
) {
|
||||
//
|
||||
}
|
||||
|
@ -0,0 +1,37 @@
|
||||
<?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('shared_environment_variables', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('key');
|
||||
$table->string('value')->nullable();
|
||||
$table->boolean('is_shown_once')->default(false);
|
||||
$table->enum('type', ['team', 'project', 'environment'])->default('team');
|
||||
|
||||
$table->foreignId('team_id')->constrained()->onDelete('cascade');
|
||||
$table->foreignId('project_id')->nullable()->constrained()->onDelete('cascade');
|
||||
$table->foreignId('environment_id')->nullable()->constrained()->onDelete('cascade');
|
||||
$table->unique(['key', 'project_id', 'team_id']);
|
||||
$table->unique(['key', 'environment_id', 'team_id']);
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('shared_environment_variables');
|
||||
}
|
||||
};
|
@ -18,6 +18,7 @@ public function run(): void
|
||||
ProjectSeeder::class,
|
||||
ProjectSettingSeeder::class,
|
||||
EnvironmentSeeder::class,
|
||||
TeamEnvironmentVariableSeeder::class,
|
||||
StandaloneDockerSeeder::class,
|
||||
SwarmDockerSeeder::class,
|
||||
KubernetesSeeder::class,
|
||||
|
36
database/seeders/SharedEnvironmentVariableSeeder.php
Normal file
36
database/seeders/SharedEnvironmentVariableSeeder.php
Normal file
@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use App\Models\SharedEnvironmentVariable;
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
class SharedEnvironmentVariableSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*/
|
||||
public function run(): void
|
||||
{
|
||||
SharedEnvironmentVariable::create([
|
||||
'key' => 'NODE_ENV',
|
||||
'value' => 'team_env',
|
||||
'type' => 'team',
|
||||
'team_id' => 0,
|
||||
]);
|
||||
SharedEnvironmentVariable::create([
|
||||
'key' => 'NODE_ENV',
|
||||
'value' => 'env_env',
|
||||
'type' => 'environment',
|
||||
'environment_id' => 1,
|
||||
'team_id' => 0,
|
||||
]);
|
||||
SharedEnvironmentVariable::create([
|
||||
'key' => 'NODE_ENV',
|
||||
'value' => 'project_env',
|
||||
'type' => 'project',
|
||||
'project_id' => 1,
|
||||
'team_id' => 0,
|
||||
]);
|
||||
}
|
||||
}
|
@ -168,3 +168,6 @@ .fullscreen {
|
||||
input.input-sm {
|
||||
@apply pr-10;
|
||||
}
|
||||
option{
|
||||
@apply text-white;
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ class="transition rounded w-11 h-11" src="{{ asset('coolify-transparent.png') }}
|
||||
</a>
|
||||
</li>
|
||||
<li title="Help us!">
|
||||
<a class="hover:bg-transparent"href="https://coolify.io/sponsorships" target="_blank">
|
||||
<a class="hover:bg-transparent" href="https://coolify.io/sponsorships" target="_blank">
|
||||
<svg class="icon hover:text-pink-500" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
|
||||
stroke-width="2">
|
||||
|
48
resources/views/components/slide-over.blade.php
Normal file
48
resources/views/components/slide-over.blade.php
Normal file
@ -0,0 +1,48 @@
|
||||
<div x-data="{
|
||||
slideOverOpen: false
|
||||
}" class="relative w-auto h-auto">
|
||||
{{ $slot }}
|
||||
<template x-teleport="body">
|
||||
<div x-show="slideOverOpen" @keydown.window.escape="slideOverOpen=false" class="relative z-[99]">
|
||||
<div x-show="slideOverOpen" @click="slideOverOpen = false" class="fixed inset-0 "></div>
|
||||
<div class="fixed inset-0 overflow-hidden">
|
||||
<div class="absolute inset-0 overflow-hidden">
|
||||
<div class="fixed inset-y-0 right-0 flex max-w-full pl-10">
|
||||
<div x-show="slideOverOpen" @click.away="slideOverOpen = false"
|
||||
x-transition:enter="transform transition ease-in-out duration-100 sm:duration-300"
|
||||
x-transition:enter-start="translate-x-full" x-transition:enter-end="translate-x-0"
|
||||
x-transition:leave="transform transition ease-in-out duration-100 sm:duration-300"
|
||||
x-transition:leave-start="translate-x-0" x-transition:leave-end="translate-x-full"
|
||||
class="w-screen max-w-md">
|
||||
<div
|
||||
class="flex flex-col h-full py-5 overflow-y-scroll border-l shadow-lg bg-primary border-neutral-800">
|
||||
<div class="px-4 sm:px-5">
|
||||
<div class="flex items-start justify-between pb-1">
|
||||
<h2 class="text-base leading-6" id="slide-over-title">
|
||||
{{ $title }}</h2>
|
||||
<div class="flex items-center h-auto ml-3">
|
||||
<button @click="slideOverOpen=false"
|
||||
class="absolute top-0 right-0 z-30 flex items-center justify-center px-3 py-2 mt-3 mr-5 space-x-1 text-xs font-normal border-none rounded">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6" fill="none"
|
||||
viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"
|
||||
class="w-3 h-3">
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="M6 18L18 6M6 6l12 12"></path>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="relative flex-1 px-4 mt-5 sm:px-5">
|
||||
<div class="absolute inset-0 px-4 sm:px-5">
|
||||
{{ $content }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
@ -14,20 +14,24 @@ class="text-warning">{{ session('currentTeam.name') }}</span></span>
|
||||
</ol>
|
||||
</nav>
|
||||
<nav class="navbar-main">
|
||||
<a class="{{ request()->routeIs('team.index') ? 'text-white' : '' }}" href="{{ route('team.index') }}">
|
||||
<a class="{{ request()->routeIs('team.index') ? 'text-white' : '' }}" href="{{ route('team.index') }}">
|
||||
<button>General</button>
|
||||
</a>
|
||||
<a class="{{ request()->routeIs('team.member.index') ? 'text-white' : '' }}" href="{{ route('team.member.index') }}">
|
||||
<a class="{{ request()->routeIs('team.member.index') ? 'text-white' : '' }}" href="{{ route('team.member.index') }}">
|
||||
<button>Members</button>
|
||||
</a>
|
||||
<a class="{{ request()->routeIs('team.storage.index') ? 'text-white' : '' }}"
|
||||
<a class="{{ request()->routeIs('team.storage.index') ? 'text-white' : '' }}"
|
||||
href="{{ route('team.storage.index') }}">
|
||||
<button>S3 Storages</button>
|
||||
</a>
|
||||
<a class="{{ request()->routeIs('team.notification.index') ? 'text-white' : '' }}"
|
||||
<a class="{{ request()->routeIs('team.notification.index') ? 'text-white' : '' }}"
|
||||
href="{{ route('team.notification.index') }}">
|
||||
<button>Notifications</button>
|
||||
</a>
|
||||
<a class="{{ request()->routeIs('team.shared-variables.index') ? 'text-white' : '' }}"
|
||||
href="{{ route('team.shared-variables.index') }}">
|
||||
<button>Shared Variables</button>
|
||||
</a>
|
||||
<div class="flex-1"></div>
|
||||
<div class="-mt-9">
|
||||
<livewire:switch-team />
|
||||
|
@ -1,13 +1,28 @@
|
||||
<div>
|
||||
<form wire:submit='submit' class="flex flex-col gap-2 ">
|
||||
<h1>Project: {{ data_get($project, 'name') }}</h1>
|
||||
<div class="pb-10">Edit project details here.</div>
|
||||
<form wire:submit='submit' class="flex flex-col gap-2 pb-10">
|
||||
<div class="flex items-end gap-2">
|
||||
<h1>Project: {{ data_get($project, 'name') }}</h1>
|
||||
<h2>General</h2>
|
||||
<x-forms.button type="submit">Save</x-forms.button>
|
||||
</div>
|
||||
<div class="pb-10">Edit project details here.</div>
|
||||
<div class="flex gap-2">
|
||||
<x-forms.input label="Name" id="project.name" />
|
||||
<x-forms.input label="Description" id="project.description" />
|
||||
</div>
|
||||
</form>
|
||||
<div class="flex gap-2">
|
||||
<h2>Shared Variables</h2>
|
||||
<x-forms.button class="btn" onclick="newVariable.showModal()">+ Add</x-forms.button>
|
||||
<livewire:project.shared.environment-variable.add />
|
||||
</div>
|
||||
<div class="pb-4">You can use this anywhere.</div>
|
||||
<div class="flex flex-col gap-2">
|
||||
@forelse ($project->environment_variables->sort()->sortBy('real_value') as $env)
|
||||
<livewire:project.shared.environment-variable.show wire:key="environment-{{ $env->id }}"
|
||||
:env="$env" type="project" />
|
||||
@empty
|
||||
<div class="text-neutral-500">No environment variables found.</div>
|
||||
@endforelse
|
||||
</div>
|
||||
</div>
|
||||
|
@ -8,7 +8,7 @@
|
||||
<ol class="flex items-center">
|
||||
<li class="inline-flex items-center">
|
||||
<a class="text-xs truncate lg:text-sm"
|
||||
href="{{ route('project.show', ['project_uuid' => request()->route('project_uuid')]) }}">
|
||||
href="{{ route('project.show', ['project_uuid' => data_get($parameters, 'project_uuid')]) }}">
|
||||
{{ $project->name }}</a>
|
||||
</li>
|
||||
<li>
|
||||
@ -20,7 +20,7 @@
|
||||
clip-rule="evenodd"></path>
|
||||
</svg>
|
||||
<a class="text-xs truncate lg:text-sm"
|
||||
href="{{ route('project.resource.index', ['environment_name' => request()->route('environment_name'), 'project_uuid' => request()->route('project_uuid')]) }}">{{ request()->route('environment_name') }}</a>
|
||||
href="{{ route('project.resource.index', ['environment_name' => data_get($parameters, 'environment_name'), 'project_uuid' => data_get($parameters, 'project_uuid')]) }}">{{ data_get($parameters, 'environment_name') }}</a>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
@ -41,4 +41,18 @@
|
||||
<x-forms.input label="Description" id="environment.description" />
|
||||
</div>
|
||||
</form>
|
||||
<div class="flex gap-2 pt-10">
|
||||
<h2>Shared Variables</h2>
|
||||
<x-forms.button class="btn" onclick="newVariable.showModal()">+ Add</x-forms.button>
|
||||
<livewire:project.shared.environment-variable.add />
|
||||
</div>
|
||||
<div class="pb-4">You can use this anywhere.</div>
|
||||
<div class="flex flex-col gap-2">
|
||||
@forelse ($environment->environment_variables->sort()->sortBy('real_value') as $env)
|
||||
<livewire:project.shared.environment-variable.show wire:key="environment-{{ $env->id }}"
|
||||
:env="$env" type="environment" />
|
||||
@empty
|
||||
<div class="text-neutral-500">No environment variables found.</div>
|
||||
@endforelse
|
||||
</div>
|
||||
</div>
|
||||
|
@ -2,15 +2,33 @@
|
||||
<div>
|
||||
<div class="flex items-center gap-2">
|
||||
<h2>Environment Variables</h2>
|
||||
<x-forms.button class="btn" onclick="newVariable.showModal()">+ Add</x-forms.button>
|
||||
<livewire:project.shared.environment-variable.add />
|
||||
@if ($resource->type() !== 'service')
|
||||
<x-slide-over>
|
||||
<x-slot:title>Add Environment Variables</x-slot:title>
|
||||
<x-slot:content>
|
||||
<form class="flex flex-col gap-2 rounded" wire:submit='submit'>
|
||||
<x-forms.input placeholder="NODE_ENV" id="key" label="Name" required />
|
||||
<x-forms.input placeholder="production" id="value" label="Value" required />
|
||||
<x-forms.button type="submit">
|
||||
Save
|
||||
</x-forms.button>
|
||||
</form>
|
||||
</x-slot:content>
|
||||
<button @click="slideOverOpen=true"
|
||||
class="font-normal text-white normal-case border-none rounded btn btn-primary btn-sm no-animation">+ Add</button>
|
||||
</x-slide-over>
|
||||
{{-- <x-forms.button class="btn" onclick="newVariable.showModal()">+ Add</x-forms.button> --}}
|
||||
@endif
|
||||
<x-forms.button
|
||||
wire:click='switch'>{{ $view === 'normal' ? 'Developer view' : 'Normal view' }}</x-forms.button>
|
||||
</div>
|
||||
<div>Environment variables (secrets) for this resource.</div>
|
||||
@if ($resource->type() === 'service')
|
||||
<div>If you cannot find a variable here, or need a new one, define it in the Docker Compose file.</div>
|
||||
@endif
|
||||
</div>
|
||||
@if ($view === 'normal')
|
||||
@forelse ($resource->environment_variables as $env)
|
||||
@forelse ($resource->environment_variables->sort()->sortBy('real_value') as $env)
|
||||
<livewire:project.shared.environment-variable.show wire:key="environment-{{ $env->id }}"
|
||||
:env="$env" :type="$resource->type()" />
|
||||
@empty
|
||||
@ -21,7 +39,7 @@
|
||||
<h3>Preview Deployments</h3>
|
||||
<div>Environment (secrets) variables for Preview Deployments.</div>
|
||||
</div>
|
||||
@foreach ($resource->environment_variables_preview as $env)
|
||||
@foreach ($resource->environment_variables_preview->sort()->sortBy('real_value') as $env)
|
||||
<livewire:project.shared.environment-variable.show wire:key="environment-{{ $env->id }}"
|
||||
:env="$env" :type="$resource->type()" />
|
||||
@endforeach
|
||||
|
@ -19,13 +19,19 @@ class="flex flex-col gap-2 p-4 m-2 border lg:items-center border-coolgray-300 lg
|
||||
@if ($isDisabled)
|
||||
<x-forms.input disabled id="env.key" />
|
||||
<x-forms.input disabled type="password" id="env.value" />
|
||||
@if ($type !== 'service')
|
||||
@if ($env->is_shared)
|
||||
<x-forms.input disabled type="password" id="env.real_value" />
|
||||
@endif
|
||||
@if ($type !== 'service' && !$isSharedVariable)
|
||||
<x-forms.checkbox instantSave id="env.is_build_time" label="Build Variable?" />
|
||||
@endif
|
||||
@else
|
||||
<x-forms.input id="env.key" />
|
||||
<x-forms.input type="password" id="env.value" />
|
||||
@if ($type !== 'service')
|
||||
@if ($env->is_shared)
|
||||
<x-forms.input disabled type="password" id="env.real_value" />
|
||||
@endif
|
||||
@if ($type !== 'service' && !$isSharedVariable)
|
||||
<x-forms.checkbox instantSave id="env.is_build_time" label="Build Variable?" />
|
||||
@endif
|
||||
@endif
|
||||
|
@ -1,5 +1,5 @@
|
||||
<div>
|
||||
<h1>Create a new Private Key</h1>
|
||||
<h2>Private Key</h2>
|
||||
<div class="subtitle ">Private Keys are used to connect to your servers without passwords.</div>
|
||||
<x-forms.button class="mb-4" wire:click="generateNewKey">Generate new SSH key for me</x-forms.button>
|
||||
<form class="flex flex-col gap-2" wire:submit='createPrivateKey'>
|
||||
|
@ -2,7 +2,7 @@
|
||||
@if ($private_keys->count() === 0)
|
||||
<h1>Create Private Key</h1>
|
||||
<div class="subtitle">You need to create a private key before you can create a server.</div>
|
||||
<livewire:private-key.create from="server" />
|
||||
<livewire:security.private-key.create from="server" />
|
||||
@else
|
||||
<livewire:server.new.by-ip :private_keys="$private_keys" :limit_reached="$limit_reached" />
|
||||
@endif
|
||||
|
@ -0,0 +1,17 @@
|
||||
<div>
|
||||
<x-team.navbar />
|
||||
<div class="flex gap-2">
|
||||
<h2>Shared Variables</h2>
|
||||
<x-forms.button class="btn" onclick="newVariable.showModal()">+ Add</x-forms.button>
|
||||
<livewire:project.shared.environment-variable.add />
|
||||
</div>
|
||||
<div class="pb-4">You can use this anywhere.</div>
|
||||
<div class="flex flex-col gap-2">
|
||||
@forelse ($team->environment_variables->sort()->sortBy('real_value') as $env)
|
||||
<livewire:project.shared.environment-variable.show wire:key="environment-{{ $env->id }}"
|
||||
:env="$env" type="team" />
|
||||
@empty
|
||||
<div class="text-neutral-500">No environment variables found.</div>
|
||||
@endforelse
|
||||
</div>
|
||||
</div>
|
@ -22,3 +22,4 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -67,6 +67,7 @@
|
||||
|
||||
use App\Livewire\Source\Github\Change as GitHubChange;
|
||||
use App\Livewire\Subscription\Index as SubscriptionIndex;
|
||||
use App\Livewire\TeamSharedVariablesIndex;
|
||||
use App\Livewire\Waitlist\Index as WaitlistIndex;
|
||||
use Illuminate\Foundation\Auth\EmailVerificationRequest;
|
||||
use Illuminate\Support\Facades\Password;
|
||||
@ -117,6 +118,7 @@
|
||||
Route::get('/new', TeamCreate::class)->name('team.create');
|
||||
Route::get('/members', TeamMemberIndex::class)->name('team.member.index');
|
||||
Route::get('/notifications', TeamNotificationIndex::class)->name('team.notification.index');
|
||||
Route::get('/shared-variables', TeamSharedVariablesIndex::class)->name('team.shared-variables.index');
|
||||
Route::get('/storages', TeamStorageIndex::class)->name('team.storage.index');
|
||||
Route::get('/storages/new', TeamStorageCreate::class)->name('team.storage.create');
|
||||
Route::get('/storages/{storage_uuid}', TeamStorageShow::class)->name('team.storage.show');
|
||||
|
@ -14,17 +14,8 @@ module.exports = {
|
||||
sans: ["Inter", "sans-serif"],
|
||||
},
|
||||
colors: {
|
||||
// applications: "#16A34A",
|
||||
// databases: "#9333EA",
|
||||
// "databases-100": "#9b46ea",
|
||||
// destinations: "#0284C7",
|
||||
// sources: "#EA580C",
|
||||
// services: "#DB2777",
|
||||
// settings: "#FEE440",
|
||||
// iam: "#C026D3",
|
||||
coollabs: "#6B16ED",
|
||||
"coollabs-100": "#7317FF",
|
||||
// coolblack: "#141414",
|
||||
"coolgray-100": "#181818",
|
||||
"coolgray-200": "#202020",
|
||||
"coolgray-300": "#242424",
|
||||
|
9
tests/Feature/Livewire/TeamSharedVariablesIndexTest.php
Normal file
9
tests/Feature/Livewire/TeamSharedVariablesIndexTest.php
Normal file
@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
use App\Livewire\TeamSharedVariablesIndex;
|
||||
use Livewire\Livewire;
|
||||
|
||||
it('renders successfully', function () {
|
||||
Livewire::test(TeamSharedVariablesIndex::class)
|
||||
->assertStatus(200);
|
||||
});
|
Loading…
Reference in New Issue
Block a user