Merge pull request #4561 from coollabsio/next

v4.0.0-beta.378
This commit is contained in:
Andras Bacsai
2024-12-13 12:20:15 +01:00
committed by GitHub
18 changed files with 134 additions and 391 deletions

View File

@@ -218,7 +218,7 @@ class Kernel extends ConsoleKernel
} }
} }
if ($service) { if ($service) {
if (str($service->status())->contains('running') === false) { if (str($service->status)->contains('running') === false) {
continue; continue;
} }
} }

View File

@@ -53,11 +53,7 @@ class ResourcesController extends Controller
$resources = $resources->flatten(); $resources = $resources->flatten();
$resources = $resources->map(function ($resource) { $resources = $resources->map(function ($resource) {
$payload = $resource->toArray(); $payload = $resource->toArray();
if ($resource->getMorphClass() === \App\Models\Service::class) { $payload['status'] = $resource->status;
$payload['status'] = $resource->status();
} else {
$payload['status'] = $resource->status;
}
$payload['type'] = $resource->type(); $payload['type'] = $resource->type();
return $payload; return $payload;

View File

@@ -154,11 +154,7 @@ class ServersController extends Controller
'created_at' => $resource->created_at, 'created_at' => $resource->created_at,
'updated_at' => $resource->updated_at, 'updated_at' => $resource->updated_at,
]; ];
if ($resource->type() === 'service') { $payload['status'] = $resource->status;
$payload['status'] = $resource->status();
} else {
$payload['status'] = $resource->status;
}
return $payload; return $payload;
}); });
@@ -237,11 +233,7 @@ class ServersController extends Controller
'created_at' => $resource->created_at, 'created_at' => $resource->created_at,
'updated_at' => $resource->updated_at, 'updated_at' => $resource->updated_at,
]; ];
if ($resource->type() === 'service') { $payload['status'] = $resource->status;
$payload['status'] = $resource->status();
} else {
$payload['status'] = $resource->status;
}
return $payload; return $payload;
}); });

View File

@@ -1072,7 +1072,7 @@ class ServicesController extends Controller
if (! $service) { if (! $service) {
return response()->json(['message' => 'Service not found.'], 404); return response()->json(['message' => 'Service not found.'], 404);
} }
if (str($service->status())->contains('running')) { if (str($service->status)->contains('running')) {
return response()->json(['message' => 'Service is already running.'], 400); return response()->json(['message' => 'Service is already running.'], 400);
} }
StartService::dispatch($service); StartService::dispatch($service);
@@ -1150,7 +1150,7 @@ class ServicesController extends Controller
if (! $service) { if (! $service) {
return response()->json(['message' => 'Service not found.'], 404); return response()->json(['message' => 'Service not found.'], 404);
} }
if (str($service->status())->contains('stopped') || str($service->status())->contains('exited')) { if (str($service->status)->contains('stopped') || str($service->status)->contains('exited')) {
return response()->json(['message' => 'Service is already stopped.'], 400); return response()->json(['message' => 'Service is already stopped.'], 400);
} }
StopService::dispatch($service); StopService::dispatch($service);

View File

@@ -27,7 +27,7 @@ class Navbar extends Component
public function mount() public function mount()
{ {
if (str($this->service->status())->contains('running') && is_null($this->service->config_hash)) { if (str($this->service->status)->contains('running') && is_null($this->service->config_hash)) {
$this->service->isConfigurationChanged(true); $this->service->isConfigurationChanged(true);
$this->dispatch('configurationChanged'); $this->dispatch('configurationChanged');
} }

View File

@@ -35,16 +35,26 @@ class SettingsOauth extends Component
}, []); }, []);
} }
private function updateOauthSettings() private function updateOauthSettings(?string $provider = null)
{ {
foreach (array_values($this->oauth_settings_map) as &$setting) { if ($provider) {
$setting->save(); $oauth = $this->oauth_settings_map[$provider];
if (! $oauth->couldBeEnabled()) {
$oauth->update(['enabled' => false]);
throw new \Exception('OAuth settings are not complete for '.$oauth->provider.'.<br/>Please fill in all required fields.');
}
$oauth->save();
$this->dispatch('success', 'OAuth settings for '.$oauth->provider.' updated successfully!');
} }
} }
public function instantSave() public function instantSave(string $provider)
{ {
$this->updateOauthSettings(); try {
$this->updateOauthSettings($provider);
} catch (\Exception $e) {
return handleError($e, $this);
}
} }
public function submit() public function submit()

View File

@@ -11,6 +11,8 @@ class OauthSetting extends Model
{ {
use HasFactory; use HasFactory;
protected $fillable = ['provider', 'client_id', 'client_secret', 'redirect_uri', 'tenant', 'base_url', 'enabled'];
protected function clientSecret(): Attribute protected function clientSecret(): Attribute
{ {
return Attribute::make( return Attribute::make(
@@ -18,4 +20,16 @@ class OauthSetting extends Model
set: fn (?string $value) => empty($value) ? null : Crypt::encryptString($value), set: fn (?string $value) => empty($value) ? null : Crypt::encryptString($value),
); );
} }
public function couldBeEnabled(): bool
{
switch ($this->provider) {
case 'azure':
return filled($this->client_id) && filled($this->client_secret) && filled($this->redirect_uri) && filled($this->tenant);
case 'authentik':
return filled($this->client_id) && filled($this->client_secret) && filled($this->redirect_uri) && filled($this->base_url);
default:
return filled($this->client_id) && filled($this->client_secret) && filled($this->redirect_uri);
}
}
} }

View File

@@ -46,7 +46,7 @@ class Service extends BaseModel
protected $guarded = []; protected $guarded = [];
protected $appends = ['server_status']; protected $appends = ['server_status', 'status'];
protected static function booted() protected static function booted()
{ {
@@ -105,12 +105,12 @@ class Service extends BaseModel
public function isRunning() public function isRunning()
{ {
return (bool) str($this->status())->contains('running'); return (bool) str($this->status)->contains('running');
} }
public function isExited() public function isExited()
{ {
return (bool) str($this->status())->contains('exited'); return (bool) str($this->status)->contains('exited');
} }
public function type() public function type()
@@ -213,7 +213,7 @@ class Service extends BaseModel
instant_remote_process(["docker network rm {$uuid}"], $server, false); instant_remote_process(["docker network rm {$uuid}"], $server, false);
} }
public function status() public function getStatusAttribute()
{ {
$applications = $this->applications; $applications = $this->applications;
$databases = $this->databases; $databases = $this->databases;

View File

@@ -3,6 +3,7 @@
namespace App\Providers; namespace App\Providers;
use App\Models\PersonalAccessToken; use App\Models\PersonalAccessToken;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Http; use Illuminate\Support\Facades\Http;
use Illuminate\Support\ServiceProvider; use Illuminate\Support\ServiceProvider;
use Illuminate\Validation\Rules\Password; use Illuminate\Validation\Rules\Password;
@@ -19,6 +20,9 @@ class AppServiceProvider extends ServiceProvider
public function boot(): void public function boot(): void
{ {
Event::listen(function (\SocialiteProviders\Manager\SocialiteWasCalled $event) {
$event->extendSocialite('authentik', \SocialiteProviders\Authentik\Provider::class);
});
Sanctum::usePersonalAccessTokenModel(PersonalAccessToken::class); Sanctum::usePersonalAccessTokenModel(PersonalAccessToken::class);
Password::defaults(function () { Password::defaults(function () {

View File

@@ -17,7 +17,7 @@ class Services extends Component
public string $complexStatus = 'exited', public string $complexStatus = 'exited',
public bool $showRefreshButton = true public bool $showRefreshButton = true
) { ) {
$this->complexStatus = $service->status(); $this->complexStatus = $service->status;
} }
/** /**

View File

@@ -2,7 +2,7 @@
return [ return [
'coolify' => [ 'coolify' => [
'version' => '4.0.0-beta.377', 'version' => '4.0.0-beta.378',
'self_hosted' => env('SELF_HOSTED', true), 'self_hosted' => env('SELF_HOSTED', true),
'autoupdate' => env('AUTOUPDATE'), 'autoupdate' => env('AUTOUPDATE'),
'base_config_path' => env('BASE_CONFIG_PATH', '/data/coolify'), 'base_config_path' => env('BASE_CONFIG_PATH', '/data/coolify'),

View File

@@ -30,4 +30,19 @@ return [
'secret' => env('AWS_SECRET_ACCESS_KEY'), 'secret' => env('AWS_SECRET_ACCESS_KEY'),
'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
], ],
'azure' => [
'client_id' => env('AZURE_CLIENT_ID'),
'client_secret' => env('AZURE_CLIENT_SECRET'),
'redirect' => env('AZURE_REDIRECT_URI'),
'tenant' => env('AZURE_TENANT_ID'),
'proxy' => env('AZURE_PROXY'),
],
'authentik' => [
'base_url' => env('AUTHENTIK_BASE_URL'),
'client_id' => env('AUTHENTIK_CLIENT_ID'),
'client_secret' => env('AUTHENTIK_CLIENT_SECRET'),
'redirect' => env('AUTHENTIK_REDIRECT_URI'),
],
]; ];

View File

@@ -0,0 +1,46 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Support\Facades\Crypt;
use Illuminate\Support\Facades\DB;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
if (DB::table('instance_settings')->exists()) {
$settings = DB::table('instance_settings')->get();
foreach ($settings as $setting) {
try {
DB::table('instance_settings')->where('id', $setting->id)->update([
'resend_api_key' => $setting->resend_api_key ? Crypt::encryptString($setting->resend_api_key) : null,
]);
} catch (Exception $e) {
\Log::error('Error encrypting resend_api_key: '.$e->getMessage());
}
}
}
}
/**
* Reverse the migrations.
*/
public function down(): void
{
if (DB::table('instance_settings')->exists()) {
$settings = DB::table('instance_settings')->get();
foreach ($settings as $setting) {
try {
DB::table('instance_settings')->where('id', $setting->id)->update([
'resend_api_key' => $setting->resend_api_key ? Crypt::decryptString($setting->resend_api_key) : null,
]);
} catch (Exception $e) {
\Log::error('Error decrypting resend_api_key: '.$e->getMessage());
}
}
}
}
};

View File

@@ -1,10 +1,10 @@
{ {
"coolify": { "coolify": {
"v4": { "v4": {
"version": "4.0.0-beta.376" "version": "4.0.0-beta.378"
}, },
"nightly": { "nightly": {
"version": "4.0.0-beta.377" "version": "4.0.0-beta.379"
}, },
"helper": { "helper": {
"version": "1.0.4" "version": "1.0.4"

View File

@@ -7,6 +7,9 @@
monacoLoader: true, monacoLoader: true,
monacoFontSize: '15px', monacoFontSize: '15px',
monacoId: $id('monaco-editor'), monacoId: $id('monaco-editor'),
isDarkMode() {
return document.documentElement.classList.contains('dark') || localStorage.theme === 'dark' || (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches);
},
monacoEditor(editor) { monacoEditor(editor) {
editor.onDidChangeModelContent((e) => { editor.onDidChangeModelContent((e) => {
this.monacoContent = editor.getValue(); this.monacoContent = editor.getValue();
@@ -41,357 +44,9 @@
let proxy = URL.createObjectURL(new Blob([` self.MonacoEnvironment = { baseUrl: 'https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.39.0/min' }; importScripts('https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.39.0/min/vs/base/worker/workerMain.min.js');`], { type: 'text/javascript' })); let proxy = URL.createObjectURL(new Blob([` self.MonacoEnvironment = { baseUrl: 'https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.39.0/min' }; importScripts('https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.39.0/min/vs/base/worker/workerMain.min.js');`], { type: 'text/javascript' }));
window.MonacoEnvironment = { getWorkerUrl: () => proxy }; window.MonacoEnvironment = { getWorkerUrl: () => proxy };
require(['vs/editor/editor.main'], () => { require(['vs/editor/editor.main'], () => {
monaco.editor.defineTheme('blackboard', {
'base': 'vs-dark',
'inherit': true,
'rules': [{
'background': editorBackground,
'token': ''
},
{
'foreground': '959da5',
'token': 'comment'
},
{
'foreground': '959da5',
'token': 'punctuation.definition.comment'
},
{
'foreground': '959da5',
'token': 'string.comment'
},
{
'foreground': 'c8e1ff',
'token': 'constant'
},
{
'foreground': 'c8e1ff',
'token': 'entity.name.constant'
},
{
'foreground': 'c8e1ff',
'token': 'variable.other.constant'
},
{
'foreground': 'c8e1ff',
'token': 'variable.language'
},
{
'foreground': 'b392f0',
'token': 'entity'
},
{
'foreground': 'b392f0',
'token': 'entity.name'
},
{
'foreground': 'f6f8fa',
'token': 'variable.parameter.function'
},
{
'foreground': '7bcc72',
'token': 'entity.name.tag'
},
{
'foreground': 'ea4a5a',
'token': 'keyword'
},
{
'foreground': 'ea4a5a',
'token': 'storage'
},
{
'foreground': 'ea4a5a',
'token': 'storage.type'
},
{
'foreground': 'f6f8fa',
'token': 'storage.modifier.package'
},
{
'foreground': 'f6f8fa',
'token': 'storage.modifier.import'
},
{
'foreground': 'f6f8fa',
'token': 'storage.type.java'
},
{
'foreground': '79b8ff',
'token': 'string'
},
{
'foreground': '79b8ff',
'token': 'punctuation.definition.string'
},
{
'foreground': '79b8ff',
'token': 'string punctuation.section.embedded source'
},
{
'foreground': 'c8e1ff',
'token': 'support'
},
{
'foreground': 'c8e1ff',
'token': 'meta.property-name'
},
{
'foreground': 'fb8532',
'token': 'variable'
},
{
'foreground': 'f6f8fa',
'token': 'variable.other'
},
{
'foreground': 'd73a49',
'fontStyle': 'bold italic underline',
'token': 'invalid.broken'
},
{
'foreground': 'd73a49',
'fontStyle': 'bold italic underline',
'token': 'invalid.deprecated'
},
{
'foreground': 'fafbfc',
'background': 'd73a49',
'fontStyle': 'italic underline',
'token': 'invalid.illegal'
},
{
'foreground': 'fafbfc',
'background': 'd73a49',
'fontStyle': 'italic underline',
'token': 'carriage-return'
},
{
'foreground': 'd73a49',
'fontStyle': 'bold italic underline',
'token': 'invalid.unimplemented'
},
{
'foreground': 'd73a49',
'token': 'message.error'
},
{
'foreground': 'f6f8fa',
'token': 'string source'
},
{
'foreground': 'c8e1ff',
'token': 'string variable'
},
{
'foreground': '79b8ff',
'token': 'source.regexp'
},
{
'foreground': '79b8ff',
'token': 'string.regexp'
},
{
'foreground': '79b8ff',
'token': 'string.regexp.character-class'
},
{
'foreground': '79b8ff',
'token': 'string.regexp constant.character.escape'
},
{
'foreground': '79b8ff',
'token': 'string.regexp source.ruby.embedded'
},
{
'foreground': '79b8ff',
'token': 'string.regexp string.regexp.arbitrary-repitition'
},
{
'foreground': '7bcc72',
'fontStyle': 'bold',
'token': 'string.regexp constant.character.escape'
},
{
'foreground': 'c8e1ff',
'token': 'support.constant'
},
{
'foreground': 'c8e1ff',
'token': 'support.variable'
},
{
'foreground': 'c8e1ff',
'token': 'meta.module-reference'
},
{
'foreground': 'fb8532',
'token': 'markup.list'
},
{
'foreground': '0366d6',
'fontStyle': 'bold',
'token': 'markup.heading'
},
{
'foreground': '0366d6',
'fontStyle': 'bold',
'token': 'markup.heading entity.name'
},
{
'foreground': 'c8e1ff',
'token': 'markup.quote'
},
{
'foreground': 'f6f8fa',
'fontStyle': 'italic',
'token': 'markup.italic'
},
{
'foreground': 'f6f8fa',
'fontStyle': 'bold',
'token': 'markup.bold'
},
{
'foreground': 'c8e1ff',
'token': 'markup.raw'
},
{
'foreground': 'b31d28',
'background': 'ffeef0',
'token': 'markup.deleted'
},
{
'foreground': 'b31d28',
'background': 'ffeef0',
'token': 'meta.diff.header.from-file'
},
{
'foreground': 'b31d28',
'background': 'ffeef0',
'token': 'punctuation.definition.deleted'
},
{
'foreground': '176f2c',
'background': 'f0fff4',
'token': 'markup.inserted'
},
{
'foreground': '176f2c',
'background': 'f0fff4',
'token': 'meta.diff.header.to-file'
},
{
'foreground': '176f2c',
'background': 'f0fff4',
'token': 'punctuation.definition.inserted'
},
{
'foreground': 'b08800',
'background': 'fffdef',
'token': 'markup.changed'
},
{
'foreground': 'b08800',
'background': 'fffdef',
'token': 'punctuation.definition.changed'
},
{
'foreground': '2f363d',
'background': '959da5',
'token': 'markup.ignored'
},
{
'foreground': '2f363d',
'background': '959da5',
'token': 'markup.untracked'
},
{
'foreground': 'b392f0',
'fontStyle': 'bold',
'token': 'meta.diff.range'
},
{
'foreground': 'c8e1ff',
'token': 'meta.diff.header'
},
{
'foreground': '0366d6',
'fontStyle': 'bold',
'token': 'meta.separator'
},
{
'foreground': '0366d6',
'token': 'meta.output'
},
{
'foreground': 'ffeef0',
'token': 'brackethighlighter.tag'
},
{
'foreground': 'ffeef0',
'token': 'brackethighlighter.curly'
},
{
'foreground': 'ffeef0',
'token': 'brackethighlighter.round'
},
{
'foreground': 'ffeef0',
'token': 'brackethighlighter.square'
},
{
'foreground': 'ffeef0',
'token': 'brackethighlighter.angle'
},
{
'foreground': 'ffeef0',
'token': 'brackethighlighter.quote'
},
{
'foreground': 'd73a49',
'token': 'brackethighlighter.unmatched'
},
{
'foreground': 'd73a49',
'token': 'sublimelinter.mark.error'
},
{
'foreground': 'fb8532',
'token': 'sublimelinter.mark.warning'
},
{
'foreground': '6a737d',
'token': 'sublimelinter.gutter-mark'
},
{
'foreground': '79b8ff',
'fontStyle': 'underline',
'token': 'constant.other.reference.link'
},
{
'foreground': '79b8ff',
'fontStyle': 'underline',
'token': 'string.other.link'
}
],
'colors': {
'editor.foreground': '#f6f8fa',
'editor.background': editorBackground,
'editor.selectionBackground': '#4c2889',
'editor.inactiveSelectionBackground': '#444d56',
'editor.lineHighlightBackground': '#444d56',
'editorCursor.foreground': '#ffffff',
'editorWhitespace.foreground': '#6a737d',
'editorIndentGuide.background': '#6a737d',
'editorIndentGuide.activeBackground': '#f6f8fa',
'editor.selectionHighlightBorder': '#444d56'
}
});
const editor = monaco.editor.create($refs.monacoEditorElement, { const editor = monaco.editor.create($refs.monacoEditorElement, {
value: monacoContent, value: monacoContent,
theme: editorTheme, theme: document.documentElement.classList.contains('dark') ? 'vs-dark' : 'vs',
wordWrap: 'on', wordWrap: 'on',
readOnly: '{{ $readonly ?? false }}', readOnly: '{{ $readonly ?? false }}',
minimap: { enabled: false }, minimap: { enabled: false },
@@ -399,7 +54,20 @@
lineNumbersMinChars: 3, lineNumbersMinChars: 3,
automaticLayout: true, automaticLayout: true,
language: '{{ $language }}' language: '{{ $language }}'
});
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (mutation.attributeName === 'class') {
const isDark = document.documentElement.classList.contains('dark');
monaco.editor.setTheme(isDark ? 'vs-dark' : 'vs');
}
});
});
observer.observe(document.documentElement, {
attributes: true,
attributeFilter: ['class']
}); });
monacoEditor(editor); monacoEditor(editor);
@@ -411,7 +79,6 @@
updatePlaceholder(editor.getValue()); updatePlaceholder(editor.getValue());
// Watch for changes in monacoContent from Livewire
$watch('monacoContent', value => { $watch('monacoContent', value => {
if (editor.getValue() !== value) { if (editor.getValue() !== value) {
editor.setValue(value); editor.setValue(value);

View File

@@ -22,7 +22,7 @@
</nav> </nav>
@if ($service->isDeployable) @if ($service->isDeployable)
<div class="flex flex-wrap order-first gap-2 items-center sm:order-last"> <div class="flex flex-wrap order-first gap-2 items-center sm:order-last">
@if (str($service->status())->contains('running')) @if (str($service->status)->contains('running'))
<x-dropdown> <x-dropdown>
<x-slot:title> <x-slot:title>
Advanced Advanced
@@ -70,7 +70,7 @@
Stop Stop
</x-slot:button-title> </x-slot:button-title>
</x-modal-confirmation> </x-modal-confirmation>
@elseif (str($service->status())->contains('degraded')) @elseif (str($service->status)->contains('degraded'))
<button @click="$wire.dispatch('startEvent')" class="gap-2 button"> <button @click="$wire.dispatch('startEvent')" class="gap-2 button">
<svg class="w-5 h-5 dark:text-warning" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> <svg class="w-5 h-5 dark:text-warning" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" <g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
@@ -99,7 +99,7 @@
Stop Stop
</x-slot:button-title> </x-slot:button-title>
</x-modal-confirmation> </x-modal-confirmation>
@elseif (str($service->status())->contains('exited')) @elseif (str($service->status)->contains('exited'))
<button wire:click='stop(true)' class="gap-2 button"> <button wire:click='stop(true)' class="gap-2 button">
<svg class="w-5 h-5" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> <svg class="w-5 h-5" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
<path fill="red" d="M26 20h-6v-2h6zm4 8h-6v-2h6zm-2-4h-6v-2h6z" /> <path fill="red" d="M26 20h-6v-2h6zm4 8h-6v-2h6zm-2-4h-6v-2h6z" />
@@ -150,8 +150,7 @@
@else @else
<div class="flex flex-wrap order-first gap-2 items-center sm:order-last"> <div class="flex flex-wrap order-first gap-2 items-center sm:order-last">
<div class="text-error"> <div class="text-error">
Unable to deploy. <a Unable to deploy. <a class="underline font-bold cursor-pointer"
class="underline font-bold cursor-pointer"
@click.prevent="activeTab = 'environment-variables'; window.location.hash = 'environment-variables'"> @click.prevent="activeTab = 'environment-variables'; window.location.hash = 'environment-variables'">
Required environment variables missing.</a> Required environment variables missing.</a>
</div> </div>

View File

@@ -18,8 +18,8 @@
<div class="p-4 border dark:border-coolgray-300"> <div class="p-4 border dark:border-coolgray-300">
<h3>{{ ucfirst($oauth_setting->provider) }}</h3> <h3>{{ ucfirst($oauth_setting->provider) }}</h3>
<div class="w-32"> <div class="w-32">
<x-forms.checkbox instantSave id="oauth_settings_map.{{ $oauth_setting->provider }}.enabled" <x-forms.checkbox instantSave="instantSave('{{ $oauth_setting->provider }}')"
label="Enabled" /> id="oauth_settings_map.{{ $oauth_setting->provider }}.enabled" label="Enabled" />
</div> </div>
<div class="flex flex-col w-full gap-2 xl:flex-row"> <div class="flex flex-col w-full gap-2 xl:flex-row">
<x-forms.input id="oauth_settings_map.{{ $oauth_setting->provider }}.client_id" <x-forms.input id="oauth_settings_map.{{ $oauth_setting->provider }}.client_id"

View File

@@ -1,10 +1,10 @@
{ {
"coolify": { "coolify": {
"v4": { "v4": {
"version": "4.0.0-beta.377" "version": "4.0.0-beta.378"
}, },
"nightly": { "nightly": {
"version": "4.0.0-beta.378" "version": "4.0.0-beta.379"
}, },
"helper": { "helper": {
"version": "1.0.4" "version": "1.0.4"