diff --git a/app/Filament/Admin/Resources/PolydockAppInstanceResource.php b/app/Filament/Admin/Resources/PolydockAppInstanceResource.php index 29245803..892be426 100644 --- a/app/Filament/Admin/Resources/PolydockAppInstanceResource.php +++ b/app/Filament/Admin/Resources/PolydockAppInstanceResource.php @@ -267,7 +267,7 @@ public static function infolist(Infolist $infolist): Infolist TextEntry::make('userGroup.name') ->label('User Group') ->visible(fn ($record) => $record->userGroup !== null) - ->url(fn ($record) => UserGroupResource::getUrl('view', ['record' => $record->userGroup])) + ->url(fn ($record) => $record->userGroup ? UserGroupResource::getUrl('view', ['record' => $record->userGroup]) : null) ->openUrlInNewTab() ->icon('heroicon-m-user-group') ->iconColor('success'), diff --git a/app/Filament/Admin/Resources/PolydockStoreAppResource.php b/app/Filament/Admin/Resources/PolydockStoreAppResource.php index e02cfe64..558d8255 100644 --- a/app/Filament/Admin/Resources/PolydockStoreAppResource.php +++ b/app/Filament/Admin/Resources/PolydockStoreAppResource.php @@ -115,6 +115,131 @@ public static function form(Form $form): Form ]) ->columns(2) ->collapsible(), + Section::make('Lagoon Scripts') + ->description('Scripts to be executed at various stages of the application lifecycle.') + ->schema([ + Section::make('Post Deploy') + ->collapsed() + ->collapsible() + ->schema([ + Forms\Components\Textarea::make('lagoon_post_deploy_script') + ->label('Script') + ->rows(3), + Grid::make(2) + ->schema([ + Forms\Components\TextInput::make('lagoon_post_deploy_service') + ->label('Service') + ->placeholder('cli'), + Forms\Components\TextInput::make('lagoon_post_deploy_container') + ->label('Container') + ->placeholder('cli'), + ]), + ]), + Section::make('Pre Upgrade') + ->collapsed() + ->collapsible() + ->schema([ + Forms\Components\Textarea::make('lagoon_pre_upgrade_script') + ->label('Script') + ->rows(3), + Grid::make(2) + ->schema([ + Forms\Components\TextInput::make('lagoon_pre_upgrade_service') + ->label('Service') + ->placeholder('cli'), + Forms\Components\TextInput::make('lagoon_pre_upgrade_container') + ->label('Container') + ->placeholder('cli'), + ]), + ]), + Section::make('Upgrade') + ->collapsed() + ->collapsible() + ->schema([ + Forms\Components\Textarea::make('lagoon_upgrade_script') + ->label('Script') + ->rows(3), + Grid::make(2) + ->schema([ + Forms\Components\TextInput::make('lagoon_upgrade_service') + ->label('Service') + ->placeholder('cli'), + Forms\Components\TextInput::make('lagoon_upgrade_container') + ->label('Container') + ->placeholder('cli'), + ]), + ]), + Section::make('Post Upgrade') + ->collapsed() + ->collapsible() + ->schema([ + Forms\Components\Textarea::make('lagoon_post_upgrade_script') + ->label('Script') + ->rows(3), + Grid::make(2) + ->schema([ + Forms\Components\TextInput::make('lagoon_post_upgrade_service') + ->label('Service') + ->placeholder('cli'), + Forms\Components\TextInput::make('lagoon_post_upgrade_container') + ->label('Container') + ->placeholder('cli'), + ]), + ]), + Section::make('Claim') + ->collapsed() + ->collapsible() + ->schema([ + Forms\Components\Textarea::make('lagoon_claim_script') + ->label('Script') + ->rows(3), + Grid::make(2) + ->schema([ + Forms\Components\TextInput::make('lagoon_claim_service') + ->label('Service') + ->placeholder('cli'), + Forms\Components\TextInput::make('lagoon_claim_container') + ->label('Container') + ->placeholder('cli'), + ]), + ]), + Section::make('Pre Remove') + ->collapsed() + ->collapsible() + ->schema([ + Forms\Components\Textarea::make('lagoon_pre_remove_script') + ->label('Script') + ->rows(3), + Grid::make(2) + ->schema([ + Forms\Components\TextInput::make('lagoon_pre_remove_service') + ->label('Service') + ->placeholder('cli'), + Forms\Components\TextInput::make('lagoon_pre_remove_container') + ->label('Container') + ->placeholder('cli'), + ]), + ]), + Section::make('Remove') + ->collapsed() + ->collapsible() + ->schema([ + Forms\Components\Textarea::make('lagoon_remove_script') + ->label('Script') + ->rows(3), + Grid::make(2) + ->schema([ + Forms\Components\TextInput::make('lagoon_remove_service') + ->label('Service') + ->placeholder('cli'), + Forms\Components\TextInput::make('lagoon_remove_container') + ->label('Container') + ->placeholder('cli'), + ]), + ]), + ]) + ->collapsible() + ->collapsed(), Section::make('App-Specific Configuration') ->description('These fields are defined by the selected App Class and will be configurable for this Store App.') ->schema(fn (Get $get): array => app(PolydockAppClassDiscovery::class) @@ -349,6 +474,91 @@ public static function infolist(Infolist $infolist): Infolist ]) ->columnSpan(1), + \Filament\Infolists\Components\Section::make('Lagoon Scripts') + ->schema([ + \Filament\Infolists\Components\Grid::make(2) + ->schema([ + TextEntry::make('lagoon_post_deploy_script') + ->label('Post Deploy Script') + ->columnSpanFull() + ->hidden(fn ($record) => blank($record->lagoon_post_deploy_script)), + TextEntry::make('lagoon_post_deploy_service') + ->label('Post Deploy Service') + ->hidden(fn ($record) => blank($record->lagoon_post_deploy_script)), + TextEntry::make('lagoon_post_deploy_container') + ->label('Post Deploy Container') + ->hidden(fn ($record) => blank($record->lagoon_post_deploy_script)), + + TextEntry::make('lagoon_pre_upgrade_script') + ->label('Pre Upgrade Script') + ->columnSpanFull() + ->hidden(fn ($record) => blank($record->lagoon_pre_upgrade_script)), + TextEntry::make('lagoon_pre_upgrade_service') + ->label('Pre Upgrade Service') + ->hidden(fn ($record) => blank($record->lagoon_pre_upgrade_script)), + TextEntry::make('lagoon_pre_upgrade_container') + ->label('Pre Upgrade Container') + ->hidden(fn ($record) => blank($record->lagoon_pre_upgrade_script)), + + TextEntry::make('lagoon_upgrade_script') + ->label('Upgrade Script') + ->columnSpanFull() + ->hidden(fn ($record) => blank($record->lagoon_upgrade_script)), + TextEntry::make('lagoon_upgrade_service') + ->label('Upgrade Service') + ->hidden(fn ($record) => blank($record->lagoon_upgrade_script)), + TextEntry::make('lagoon_upgrade_container') + ->label('Upgrade Container') + ->hidden(fn ($record) => blank($record->lagoon_upgrade_script)), + + TextEntry::make('lagoon_post_upgrade_script') + ->label('Post Upgrade Script') + ->columnSpanFull() + ->hidden(fn ($record) => blank($record->lagoon_post_upgrade_script)), + TextEntry::make('lagoon_post_upgrade_service') + ->label('Post Upgrade Service') + ->hidden(fn ($record) => blank($record->lagoon_post_upgrade_script)), + TextEntry::make('lagoon_post_upgrade_container') + ->label('Post Upgrade Container') + ->hidden(fn ($record) => blank($record->lagoon_post_upgrade_script)), + + TextEntry::make('lagoon_claim_script') + ->label('Claim Script') + ->columnSpanFull() + ->hidden(fn ($record) => blank($record->lagoon_claim_script)), + TextEntry::make('lagoon_claim_service') + ->label('Claim Service') + ->hidden(fn ($record) => blank($record->lagoon_claim_script)), + TextEntry::make('lagoon_claim_container') + ->label('Claim Container') + ->hidden(fn ($record) => blank($record->lagoon_claim_script)), + + TextEntry::make('lagoon_pre_remove_script') + ->label('Pre Remove Script') + ->columnSpanFull() + ->hidden(fn ($record) => blank($record->lagoon_pre_remove_script)), + TextEntry::make('lagoon_pre_remove_service') + ->label('Pre Remove Service') + ->hidden(fn ($record) => blank($record->lagoon_pre_remove_script)), + TextEntry::make('lagoon_pre_remove_container') + ->label('Pre Remove Container') + ->hidden(fn ($record) => blank($record->lagoon_pre_remove_script)), + + TextEntry::make('lagoon_remove_script') + ->label('Remove Script') + ->columnSpanFull() + ->hidden(fn ($record) => blank($record->lagoon_remove_script)), + TextEntry::make('lagoon_remove_service') + ->label('Remove Service') + ->hidden(fn ($record) => blank($record->lagoon_remove_script)), + TextEntry::make('lagoon_remove_container') + ->label('Remove Container') + ->hidden(fn ($record) => blank($record->lagoon_remove_script)), + ]), + ]) + ->collapsible() + ->columnSpanFull(), + \Filament\Infolists\Components\Section::make('App-Specific Configuration') ->schema(fn ($record): array => app(PolydockAppClassDiscovery::class) ->getStoreAppInfolistSchema($record->polydock_app_class ?? '')) diff --git a/app/Filament/Admin/Resources/UserGroupResource.php b/app/Filament/Admin/Resources/UserGroupResource.php index f94fa533..8c5cfa5a 100644 --- a/app/Filament/Admin/Resources/UserGroupResource.php +++ b/app/Filament/Admin/Resources/UserGroupResource.php @@ -5,6 +5,7 @@ use App\Filament\Admin\Resources\UserGroupResource\Pages; use App\Filament\Admin\Resources\UserGroupResource\RelationManagers; use App\Models\UserGroup; +use Filament\Forms\Components\TextInput; use Filament\Forms\Form; use Filament\Infolists\Components\Grid; use Filament\Infolists\Components\Section; @@ -32,7 +33,9 @@ public static function form(Form $form): Form { return $form ->schema([ - // + TextInput::make('name') + ->required() + ->maxLength(255), ]); } diff --git a/app/Filament/Admin/Resources/UserGroupResource/RelationManagers/UsersRelationManager.php b/app/Filament/Admin/Resources/UserGroupResource/RelationManagers/UsersRelationManager.php index 64270564..217bed5f 100644 --- a/app/Filament/Admin/Resources/UserGroupResource/RelationManagers/UsersRelationManager.php +++ b/app/Filament/Admin/Resources/UserGroupResource/RelationManagers/UsersRelationManager.php @@ -4,9 +4,12 @@ namespace App\Filament\Admin\Resources\UserGroupResource\RelationManagers; +use App\Enums\UserGroupRoleEnum; use App\Filament\Admin\Resources\UserResource; +use App\Models\User; use Filament\Forms; use Filament\Forms\Form; +use Filament\Forms\Get; use Filament\Resources\RelationManagers\RelationManager; use Filament\Tables; use Filament\Tables\Table; @@ -15,31 +18,51 @@ class UsersRelationManager extends RelationManager { protected static string $relationship = 'users'; + protected static ?string $inverseRelationship = 'groups'; + #[\Override] public function form(Form $form): Form { return $form ->schema([ - Forms\Components\TextInput::make('name') + Forms\Components\TextInput::make('first_name') + ->required() + ->maxLength(255), + Forms\Components\TextInput::make('last_name') ->required() ->maxLength(255), Forms\Components\TextInput::make('email') ->required() ->email() + ->unique(ignoreRecord: true) ->maxLength(255), + Forms\Components\TextInput::make('password') + ->password() + ->dehydrated(fn ($state) => filled($state)) + ->required(fn (string $operation): bool => $operation === 'create') + ->maxLength(255) + ->label(fn (string $operation): string => $operation === 'create' + ? 'Password' + : 'New Password (leave blank to keep current)'), + Forms\Components\Select::make('role') + ->options(UserGroupRoleEnum::class) + ->required(), ]); } public function table(Table $table): Table { return $table - ->recordTitleAttribute('name') + ->recordTitleAttribute('email') ->columns([ Tables\Columns\TextColumn::make('name') + ->label('Name') ->formatStateUsing(fn ($record) => "{$record->first_name} {$record->last_name}") ->url(fn ($record) => UserResource::getUrl('view', ['record' => $record])) ->openUrlInNewTab(), Tables\Columns\TextColumn::make('email'), + Tables\Columns\TextColumn::make('pivot.role') + ->badge(), Tables\Columns\TextColumn::make('groups_count') ->counts('groups') ->label('Groups'), @@ -48,14 +71,116 @@ public function table(Table $table): Table // ]) ->headerActions([ - Tables\Actions\CreateAction::make(), + Tables\Actions\Action::make('add_user') + ->label('Add User') + ->modalHeading('Add User to Group') + ->modalWidth('lg') + ->form([ + Forms\Components\ToggleButtons::make('mode') + ->label('User Source') + ->options([ + 'existing' => 'Existing User', + 'new' => 'Create New User', + ]) + ->icons([ + 'existing' => 'heroicon-m-user', + 'new' => 'heroicon-m-user-plus', + ]) + ->default('existing') + ->colors([ + 'existing' => 'primary', + 'new' => 'success', + ]) + ->grouped() + ->live(), + + Forms\Components\Section::make('Search Existing User') + ->visible(fn (Get $get) => $get('mode') === 'existing') + ->schema([ + Forms\Components\Select::make('user_id') + ->label('Select User') + ->placeholder('Search by email...') + ->searchable() + ->getSearchResultsUsing(function (string $search): array { + $alreadyInGroup = $this->getRelationship()->pluck('users.id')->toArray(); + + return User::query() + ->whereNotIn('id', $alreadyInGroup) + ->where('email', 'like', '%'.$search.'%') + ->limit(50) + ->pluck('email', 'id') + ->toArray(); + }) + ->getOptionLabelUsing(function ($value): ?string { + if ($value === null) { + return null; + } + + return User::query() + ->whereKey($value) + ->value('email'); + }) + ->required(fn (Get $get) => $get('mode') === 'existing'), + ]), + + Forms\Components\Section::make('Create New User') + ->visible(fn (Get $get) => $get('mode') === 'new') + ->schema([ + Forms\Components\Grid::make(2) + ->schema([ + Forms\Components\TextInput::make('first_name') + ->label('First Name') + ->required(fn (Get $get) => $get('mode') === 'new') + ->maxLength(255), + Forms\Components\TextInput::make('last_name') + ->label('Last Name') + ->required(fn (Get $get) => $get('mode') === 'new') + ->maxLength(255), + ]), + Forms\Components\TextInput::make('email') + ->label('Email Address') + ->email() + ->required(fn (Get $get) => $get('mode') === 'new') + ->unique('users', 'email') + ->maxLength(255), + Forms\Components\TextInput::make('password') + ->label('Password') + ->password() + ->required(fn (Get $get) => $get('mode') === 'new') + ->maxLength(255), + ]), + + Forms\Components\Select::make('role') + ->options(UserGroupRoleEnum::class) + ->default(UserGroupRoleEnum::MEMBER) + ->required(), + ]) + ->action(function (array $data, RelationManager $livewire) { + $userId = $data['user_id'] ?? null; + + if ($data['mode'] === 'new') { + $user = User::create([ + 'first_name' => $data['first_name'], + 'last_name' => $data['last_name'], + 'email' => $data['email'], + 'password' => $data['password'], + ]); + $userId = $user->id; + } + + if ($userId) { + $livewire->getRelationship()->attach($userId, ['role' => $data['role']]); + } + }), ]) ->actions([ Tables\Actions\EditAction::make(), + Tables\Actions\DetachAction::make(), Tables\Actions\DeleteAction::make(), ]) ->bulkActions([ Tables\Actions\BulkActionGroup::make([ + Tables\Actions\DetachBulkAction::make(), Tables\Actions\DeleteBulkAction::make(), ]), ]); diff --git a/app/Filament/Admin/Resources/UserResource.php b/app/Filament/Admin/Resources/UserResource.php index 8e39a872..bdbc9f7f 100644 --- a/app/Filament/Admin/Resources/UserResource.php +++ b/app/Filament/Admin/Resources/UserResource.php @@ -160,7 +160,7 @@ public static function infolist(Infolist $infolist): Infolist ->label('Member of') ->listWithLineBreaks() ->bulleted() - ->url(fn ($record, $state) => UserGroupResource::getUrl('view', ['record' => $record->groups->first()])) + ->url(fn ($record) => $record->groups->isNotEmpty() ? UserGroupResource::getUrl('view', ['record' => $record->groups->first()]) : null) ->openUrlInNewTab(), ]) ->columnSpan(1), diff --git a/app/Http/Controllers/Api/AuthenticatedApiController.php b/app/Http/Controllers/Api/AuthenticatedApiController.php index 2518edd0..838957f2 100644 --- a/app/Http/Controllers/Api/AuthenticatedApiController.php +++ b/app/Http/Controllers/Api/AuthenticatedApiController.php @@ -273,8 +273,16 @@ function ($attribute, $value, $fail) { * @response { * "data": { * "uuid": "3a105da1-9c87-43ca-9ac8-72787fc5e315", + * "name": "my-instance", * "status": "running-healthy-claimed", * "status_message": "Instance is running smoothly.", + * "app_url": "https://my-instance.example.com", + * "store_app": { + * "uuid": "7b206eb2-1d98-54db-0bd9-83898gd6f426", + * "name": "My App", + * "git_url": "git@github.com:example/repo.git" + * }, + * "created_at": "2025-01-01T00:00:00.000000Z", * "lagoon_claim_script": "/lagoon/polydock_claim.sh", * "lagoon_project_name": "example-project" * } @@ -282,16 +290,33 @@ function ($attribute, $value, $fail) { */ public function getInstanceStatus(string $uuid): JsonResponse { - $instance = PolydockAppInstance::where('uuid', $uuid)->firstOrFail(); + $instance = PolydockAppInstance::where('uuid', $uuid)->with('storeApp')->firstOrFail(); - return response()->json([ - 'data' => [ - 'uuid' => $instance->uuid, - 'status' => $instance->status?->value, - 'status_message' => $instance->status_message, - 'lagoon_claim_script' => $instance->getKeyValue(key: 'lagoon-claim-script'), - 'lagoon_project_name' => $instance->getKeyValue(key: 'lagoon-project-name'), + $data = [ + 'uuid' => $instance->uuid, + 'name' => $instance->name, + 'status' => $instance->status?->value, + 'status_message' => $instance->status_message, + 'app_url' => $instance->app_url, + 'store_app' => [ + 'uuid' => $instance->storeApp->uuid, + 'name' => $instance->storeApp->name, + 'git_url' => $instance->storeApp->lagoon_deploy_git, ], + 'created_at' => $instance->created_at, + 'lagoon_claim_script' => $instance->getKeyValue(key: 'lagoon-claim-script'), + 'lagoon_project_name' => $instance->getKeyValue(key: 'lagoon-project-name'), + ]; + + // Include credentials if they exist and instance is at least in claimed status + if ($instance->status === PolydockAppInstanceStatus::RUNNING_HEALTHY_CLAIMED) { + $data['app_admin_username'] = $instance->getGeneratedAppAdminUsername(); + $data['app_admin_password'] = $instance->getGeneratedAppAdminPassword(); + $data['app_admin_api_key'] = $instance->getKeyValue('app-admin-api-key'); + } + + return response()->json([ + 'data' => $data, ]); } diff --git a/app/Listeners/ProcessPolydockAppInstanceStatusChange.php b/app/Listeners/ProcessPolydockAppInstanceStatusChange.php index 9a3ed846..f3985239 100644 --- a/app/Listeners/ProcessPolydockAppInstanceStatusChange.php +++ b/app/Listeners/ProcessPolydockAppInstanceStatusChange.php @@ -61,6 +61,13 @@ public function handle(PolydockAppInstanceStatusChanged $event): void ); } + if ($appInstance->getKeyValue('app-admin-api-key')) { + $event->appInstance->remoteRegistration->setResultValue( + 'app_admin_api_key', + $appInstance->getKeyValue('app-admin-api-key'), + ); + } + // Store user information in registration results if ($appInstance->getKeyValue('user-first-name')) { $event->appInstance->remoteRegistration->setResultValue( diff --git a/app/Models/PolydockVariable.php b/app/Models/PolydockVariable.php index ca93426b..372fc391 100644 --- a/app/Models/PolydockVariable.php +++ b/app/Models/PolydockVariable.php @@ -3,9 +3,11 @@ namespace App\Models; use App\Enums\PolydockVariableScopeEnum; +use Illuminate\Contracts\Encryption\DecryptException; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\MorphTo; use Illuminate\Support\Facades\Crypt; +use Illuminate\Support\Facades\Log; class PolydockVariable extends Model { @@ -29,7 +31,17 @@ public function getDecryptedValue(): ?string return null; } - return $this->is_encrypted ? Crypt::decryptString($this->value) : $this->value; + if ($this->is_encrypted) { + try { + return Crypt::decryptString($this->value); + } catch (DecryptException $e) { + Log::error('PolydockVariable decryption failed for variable name: '.($this->name ?? 'unknown').'. Error: '.$e->getMessage()); + + return null; + } + } + + return $this->value; } /** diff --git a/composer.json b/composer.json index e4379dd9..4e349011 100644 --- a/composer.json +++ b/composer.json @@ -12,7 +12,7 @@ "php": "^8.3", "ext-curl": "*", "amazeeio/lagoon-logs": "^0.0.5", - "amazeeio/polydock-app-amazeeclaw": "^0.1", + "amazeeio/polydock-app-amazeeclaw": "^0.1.11", "amazeeio/polydock-app-amazeeio-privategpt": "^0.1", "dedoc/scramble": "^0.13.16", "evanschleret/lara-mjml": "^0.3.0", @@ -24,8 +24,10 @@ "laravel/framework": "^12.0", "laravel/horizon": "^5.30", "laravel/sanctum": "^4.0", + "laravel/slack-notification-channel": "^3.8", "laravel/tinker": "^2.9", "phpseclib/phpseclib": "^3.0", + "spatie/laravel-slack-alerts": "^1.9", "spatie/mjml-php": "^1.2" }, "require-dev": { diff --git a/composer.lock b/composer.lock index f088b32a..ef0bf061 100644 --- a/composer.lock +++ b/composer.lock @@ -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": "9014550b8b9f780e4a2ff235bf4dbcca", + "content-hash": "fbba65f5c99e0d3e84b588b6648fa373", "packages": [ { "name": "amazeeio/lagoon-logs", @@ -55,16 +55,16 @@ }, { "name": "amazeeio/polydock-app-amazeeclaw", - "version": "v0.1.8", + "version": "v0.1.11", "source": { "type": "git", "url": "https://github.com/amazeeio/polydock-app-amazeeclaw.git", - "reference": "c43274ae5042acd1a6a36167d74f8ab59ce63338" + "reference": "332d956eba34bf8f0fb708383f169594c49c32f1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/amazeeio/polydock-app-amazeeclaw/zipball/c43274ae5042acd1a6a36167d74f8ab59ce63338", - "reference": "c43274ae5042acd1a6a36167d74f8ab59ce63338", + "url": "https://api.github.com/repos/amazeeio/polydock-app-amazeeclaw/zipball/332d956eba34bf8f0fb708383f169594c49c32f1", + "reference": "332d956eba34bf8f0fb708383f169594c49c32f1", "shasum": "" }, "require": { @@ -83,9 +83,9 @@ "description": "Polydock App - AmazeeClaw AI", "support": { "issues": "https://github.com/amazeeio/polydock-app-amazeeclaw/issues", - "source": "https://github.com/amazeeio/polydock-app-amazeeclaw/tree/v0.1.8" + "source": "https://github.com/amazeeio/polydock-app-amazeeclaw/tree/v0.1.11" }, - "time": "2026-03-20T22:03:00+00:00" + "time": "2026-03-23T19:47:52+00:00" }, { "name": "amazeeio/polydock-app-amazeeio-privategpt", @@ -486,16 +486,16 @@ }, { "name": "cuyz/valinor", - "version": "2.3.2", + "version": "2.4.0", "source": { "type": "git", "url": "https://github.com/CuyZ/Valinor.git", - "reference": "3b9f3f54901371d589776502aab3da3a046801a7" + "reference": "3b0afa3a287ed7f3a69aab223726cf1139454c34" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/CuyZ/Valinor/zipball/3b9f3f54901371d589776502aab3da3a046801a7", - "reference": "3b9f3f54901371d589776502aab3da3a046801a7", + "url": "https://api.github.com/repos/CuyZ/Valinor/zipball/3b0afa3a287ed7f3a69aab223726cf1139454c34", + "reference": "3b0afa3a287ed7f3a69aab223726cf1139454c34", "shasum": "" }, "require": { @@ -507,14 +507,15 @@ }, "require-dev": { "friendsofphp/php-cs-fixer": "^3.91", - "infection/infection": "^0.31", + "infection/infection": "^0.32", "marcocesarato/php-conventional-changelog": "^1.12", "mikey179/vfsstream": "^1.6.10", "phpbench/phpbench": "^1.3", "phpstan/phpstan": "^2.0", "phpstan/phpstan-phpunit": "^2.0", "phpstan/phpstan-strict-rules": "^2.0", - "phpunit/phpunit": "^10.5", + "phpunit/phpunit": "^11.5", + "psr/http-message": "^2.0", "rector/rector": "^2.0", "vimeo/psalm": "^6.0" }, @@ -550,7 +551,7 @@ ], "support": { "issues": "https://github.com/CuyZ/Valinor/issues", - "source": "https://github.com/CuyZ/Valinor/tree/2.3.2" + "source": "https://github.com/CuyZ/Valinor/tree/2.4.0" }, "funding": [ { @@ -558,7 +559,7 @@ "type": "github" } ], - "time": "2026-01-23T15:26:34+00:00" + "time": "2026-03-23T17:38:05+00:00" }, { "name": "danharrin/date-format-converter", @@ -1896,16 +1897,16 @@ }, { "name": "freedomtech-hosting/polydock-app-amazeeio-generic", - "version": "v0.1.5", + "version": "v0.1.7", "source": { "type": "git", "url": "https://github.com/amazeeio/polydock-app-amazeeio-generic.git", - "reference": "3dff1aaaf781862bd877440438ed12e90c31bb3a" + "reference": "aba294a9a7b8991cb3acac4ba3360cc3fa75897b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/amazeeio/polydock-app-amazeeio-generic/zipball/3dff1aaaf781862bd877440438ed12e90c31bb3a", - "reference": "3dff1aaaf781862bd877440438ed12e90c31bb3a", + "url": "https://api.github.com/repos/amazeeio/polydock-app-amazeeio-generic/zipball/aba294a9a7b8991cb3acac4ba3360cc3fa75897b", + "reference": "aba294a9a7b8991cb3acac4ba3360cc3fa75897b", "shasum": "" }, "require": { @@ -1932,9 +1933,9 @@ "description": "Polydock App - amazee.io Generic", "support": { "issues": "https://github.com/amazeeio/polydock-app-amazeeio-generic/issues", - "source": "https://github.com/amazeeio/polydock-app-amazeeio-generic/tree/v0.1.5" + "source": "https://github.com/amazeeio/polydock-app-amazeeio-generic/tree/v0.1.7" }, - "time": "2026-03-20T22:24:56+00:00" + "time": "2026-03-23T18:05:50+00:00" }, { "name": "fruitcake/php-cors", @@ -3088,6 +3089,71 @@ }, "time": "2026-02-20T19:59:49+00:00" }, + { + "name": "laravel/slack-notification-channel", + "version": "v3.8.0", + "source": { + "type": "git", + "url": "https://github.com/laravel/slack-notification-channel.git", + "reference": "3c27f6e851570274255809f4d3939ff5c2ace82b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/slack-notification-channel/zipball/3c27f6e851570274255809f4d3939ff5c2ace82b", + "reference": "3c27f6e851570274255809f4d3939ff5c2ace82b", + "shasum": "" + }, + "require": { + "guzzlehttp/guzzle": "^7.0", + "illuminate/http": "^9.0|^10.0|^11.0|^12.0|^13.0", + "illuminate/notifications": "^9.0|^10.0|^11.0|^12.0|^13.0", + "illuminate/support": "^9.0|^10.0|^11.0|^12.0|^13.0", + "php": "^8.0" + }, + "require-dev": { + "mockery/mockery": "^1.0", + "orchestra/testbench": "^7.0|^8.0|^9.0|^10.0|^11.0", + "phpstan/phpstan": "^1.10|^2.0", + "phpunit/phpunit": "^9.0|^10.4|^11.5|^12.0|^13.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Illuminate\\Notifications\\SlackChannelServiceProvider" + ] + }, + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Illuminate\\Notifications\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "Slack Notification Channel for laravel.", + "keywords": [ + "laravel", + "notifications", + "slack" + ], + "support": { + "issues": "https://github.com/laravel/slack-notification-channel/issues", + "source": "https://github.com/laravel/slack-notification-channel/tree/v3.8.0" + }, + "time": "2026-03-17T16:40:49+00:00" + }, { "name": "laravel/tinker", "version": "v2.11.1", @@ -5490,16 +5556,16 @@ }, { "name": "psy/psysh", - "version": "v0.12.21", + "version": "v0.12.22", "source": { "type": "git", "url": "https://github.com/bobthecow/psysh.git", - "reference": "4821fab5b7cd8c49a673a9fd5754dc9162bb9e97" + "reference": "3be75d5b9244936dd4ac62ade2bfb004d13acf0f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/bobthecow/psysh/zipball/4821fab5b7cd8c49a673a9fd5754dc9162bb9e97", - "reference": "4821fab5b7cd8c49a673a9fd5754dc9162bb9e97", + "url": "https://api.github.com/repos/bobthecow/psysh/zipball/3be75d5b9244936dd4ac62ade2bfb004d13acf0f", + "reference": "3be75d5b9244936dd4ac62ade2bfb004d13acf0f", "shasum": "" }, "require": { @@ -5563,9 +5629,9 @@ ], "support": { "issues": "https://github.com/bobthecow/psysh/issues", - "source": "https://github.com/bobthecow/psysh/tree/v0.12.21" + "source": "https://github.com/bobthecow/psysh/tree/v0.12.22" }, - "time": "2026-03-06T21:21:28+00:00" + "time": "2026-03-22T23:03:24+00:00" }, { "name": "ralouphie/getallheaders", @@ -6123,6 +6189,88 @@ ], "time": "2026-02-21T12:49:54+00:00" }, + { + "name": "spatie/laravel-slack-alerts", + "version": "1.9.0", + "source": { + "type": "git", + "url": "https://github.com/spatie/laravel-slack-alerts.git", + "reference": "ec6c764c73576ee06dc21a06c0219cf4297f2bfb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/laravel-slack-alerts/zipball/ec6c764c73576ee06dc21a06c0219cf4297f2bfb", + "reference": "ec6c764c73576ee06dc21a06c0219cf4297f2bfb", + "shasum": "" + }, + "require": { + "illuminate/contracts": "^12.0|^13.0", + "php": "^8.2", + "spatie/laravel-package-tools": "^1.9.2" + }, + "require-dev": { + "nunomaduro/collision": "^8.0", + "orchestra/testbench": "^10.0|^11.0", + "pestphp/pest": "^3.0|^4.0", + "pestphp/pest-plugin-laravel": "^3.0|^4.0", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan-deprecation-rules": "^1.0|^2.0", + "phpstan/phpstan-phpunit": "^1.0|^2.0", + "phpunit/phpunit": "^11.0|^12.0", + "spatie/laravel-ray": "^1.26" + }, + "type": "library", + "extra": { + "laravel": { + "aliases": { + "Slack": "SlackAlert" + }, + "providers": [ + "Spatie\\SlackAlerts\\SlackAlertsServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Spatie\\SlackAlerts\\": "src", + "Spatie\\SlackAlerts\\Database\\Factories\\": "database/factories" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Niels Vanpachtenbeke", + "email": "niels@spatie.be", + "role": "Developer" + }, + { + "name": "Freek Van der Herten", + "email": "freek@spatie.be", + "role": "Developer" + } + ], + "description": "Send a message to Slack", + "homepage": "https://github.com/spatie/laravel-slack-alerts", + "keywords": [ + "laravel", + "laravel-slack-alerts", + "spatie" + ], + "support": { + "issues": "https://github.com/spatie/laravel-slack-alerts/issues", + "source": "https://github.com/spatie/laravel-slack-alerts/tree/1.9.0" + }, + "funding": [ + { + "url": "https://github.com/spatie", + "type": "github" + } + ], + "time": "2026-03-18T09:08:09+00:00" + }, { "name": "spatie/mjml-php", "version": "1.2.6", diff --git a/config/logging.php b/config/logging.php index eed5d378..58273175 100644 --- a/config/logging.php +++ b/config/logging.php @@ -7,6 +7,20 @@ use Monolog\Handler\SyslogUdpHandler; use Monolog\Processor\PsrLogMessageProcessor; +$stackChannels = explode(',', (string) env('LOG_STACK', 'single')); + +$lagoonChannel = env('LAGOON_LOGS_CHANNEL'); + +if (env('LAGOON_PROJECT') && $lagoonChannel) { + $stackChannels[] = $lagoonChannel; +} + +if (env('LOG_SLACK_WEBHOOK_URL')) { + $stackChannels[] = 'slack'; +} + +$stackChannels = array_unique(array_filter($stackChannels)); + return [ /* @@ -56,7 +70,7 @@ 'stack' => [ 'driver' => 'stack', - 'channels' => explode(',', (string) env('LOG_STACK', 'single')), + 'channels' => $stackChannels, 'ignore_exceptions' => false, ], diff --git a/database/factories/UserGroupFactory.php b/database/factories/UserGroupFactory.php index bf1f71d6..25e671d2 100644 --- a/database/factories/UserGroupFactory.php +++ b/database/factories/UserGroupFactory.php @@ -20,8 +20,7 @@ class UserGroupFactory extends Factory public function definition(): array { return [ - 'id' => fake()->text(), - 'name' => fake()->name(), + 'name' => fake()->company(), 'created_at' => $this->faker->dateTime(), 'updated_at' => $this->faker->dateTime(), ]; diff --git a/package-lock.json b/package-lock.json index ba88e54a..9a386c25 100644 --- a/package-lock.json +++ b/package-lock.json @@ -592,9 +592,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.59.0.tgz", - "integrity": "sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==", + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.0.tgz", + "integrity": "sha512-WOhNW9K8bR3kf4zLxbfg6Pxu2ybOUbB2AjMDHSQx86LIF4rH4Ft7vmMwNt0loO0eonglSNy4cpD3MKXXKQu0/A==", "cpu": [ "arm" ], @@ -606,9 +606,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.59.0.tgz", - "integrity": "sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==", + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.0.tgz", + "integrity": "sha512-u6JHLll5QKRvjciE78bQXDmqRqNs5M/3GVqZeMwvmjaNODJih/WIrJlFVEihvV0MiYFmd+ZyPr9wxOVbPAG2Iw==", "cpu": [ "arm64" ], @@ -620,9 +620,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.59.0.tgz", - "integrity": "sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==", + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.0.tgz", + "integrity": "sha512-qEF7CsKKzSRc20Ciu2Zw1wRrBz4g56F7r/vRwY430UPp/nt1x21Q/fpJ9N5l47WWvJlkNCPJz3QRVw008fi7yA==", "cpu": [ "arm64" ], @@ -634,9 +634,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.59.0.tgz", - "integrity": "sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==", + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.0.tgz", + "integrity": "sha512-WADYozJ4QCnXCH4wPB+3FuGmDPoFseVCUrANmA5LWwGmC6FL14BWC7pcq+FstOZv3baGX65tZ378uT6WG8ynTw==", "cpu": [ "x64" ], @@ -648,9 +648,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.59.0.tgz", - "integrity": "sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==", + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.0.tgz", + "integrity": "sha512-6b8wGHJlDrGeSE3aH5mGNHBjA0TTkxdoNHik5EkvPHCt351XnigA4pS7Wsj/Eo9Y8RBU6f35cjN9SYmCFBtzxw==", "cpu": [ "arm64" ], @@ -662,9 +662,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.59.0.tgz", - "integrity": "sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==", + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.0.tgz", + "integrity": "sha512-h25Ga0t4jaylMB8M/JKAyrvvfxGRjnPQIR8lnCayyzEjEOx2EJIlIiMbhpWxDRKGKF8jbNH01NnN663dH638mA==", "cpu": [ "x64" ], @@ -676,9 +676,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.59.0.tgz", - "integrity": "sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==", + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.0.tgz", + "integrity": "sha512-RzeBwv0B3qtVBWtcuABtSuCzToo2IEAIQrcyB/b2zMvBWVbjo8bZDjACUpnaafaxhTw2W+imQbP2BD1usasK4g==", "cpu": [ "arm" ], @@ -690,9 +690,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.59.0.tgz", - "integrity": "sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==", + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.0.tgz", + "integrity": "sha512-Sf7zusNI2CIU1HLzuu9Tc5YGAHEZs5Lu7N1ssJG4Tkw6e0MEsN7NdjUDDfGNHy2IU+ENyWT+L2obgWiguWibWQ==", "cpu": [ "arm" ], @@ -704,9 +704,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.59.0.tgz", - "integrity": "sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==", + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.0.tgz", + "integrity": "sha512-DX2x7CMcrJzsE91q7/O02IJQ5/aLkVtYFryqCjduJhUfGKG6yJV8hxaw8pZa93lLEpPTP/ohdN4wFz7yp/ry9A==", "cpu": [ "arm64" ], @@ -718,9 +718,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.59.0.tgz", - "integrity": "sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==", + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.0.tgz", + "integrity": "sha512-09EL+yFVbJZlhcQfShpswwRZ0Rg+z/CsSELFCnPt3iK+iqwGsI4zht3secj5vLEs957QvFFXnzAT0FFPIxSrkQ==", "cpu": [ "arm64" ], @@ -732,9 +732,9 @@ ] }, "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.59.0.tgz", - "integrity": "sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==", + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.0.tgz", + "integrity": "sha512-i9IcCMPr3EXm8EQg5jnja0Zyc1iFxJjZWlb4wr7U2Wx/GrddOuEafxRdMPRYVaXjgbhvqalp6np07hN1w9kAKw==", "cpu": [ "loong64" ], @@ -746,9 +746,9 @@ ] }, "node_modules/@rollup/rollup-linux-loong64-musl": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.59.0.tgz", - "integrity": "sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==", + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.0.tgz", + "integrity": "sha512-DGzdJK9kyJ+B78MCkWeGnpXJ91tK/iKA6HwHxF4TAlPIY7GXEvMe8hBFRgdrR9Ly4qebR/7gfUs9y2IoaVEyog==", "cpu": [ "loong64" ], @@ -760,9 +760,9 @@ ] }, "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.59.0.tgz", - "integrity": "sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==", + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.0.tgz", + "integrity": "sha512-RwpnLsqC8qbS8z1H1AxBA1H6qknR4YpPR9w2XX0vo2Sz10miu57PkNcnHVaZkbqyw/kUWfKMI73jhmfi9BRMUQ==", "cpu": [ "ppc64" ], @@ -774,9 +774,9 @@ ] }, "node_modules/@rollup/rollup-linux-ppc64-musl": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.59.0.tgz", - "integrity": "sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==", + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.0.tgz", + "integrity": "sha512-Z8pPf54Ly3aqtdWC3G4rFigZgNvd+qJlOE52fmko3KST9SoGfAdSRCwyoyG05q1HrrAblLbk1/PSIV+80/pxLg==", "cpu": [ "ppc64" ], @@ -788,9 +788,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.59.0.tgz", - "integrity": "sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==", + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.0.tgz", + "integrity": "sha512-3a3qQustp3COCGvnP4SvrMHnPQ9d1vzCakQVRTliaz8cIp/wULGjiGpbcqrkv0WrHTEp8bQD/B3HBjzujVWLOA==", "cpu": [ "riscv64" ], @@ -802,9 +802,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.59.0.tgz", - "integrity": "sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==", + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.0.tgz", + "integrity": "sha512-pjZDsVH/1VsghMJ2/kAaxt6dL0psT6ZexQVrijczOf+PeP2BUqTHYejk3l6TlPRydggINOeNRhvpLa0AYpCWSQ==", "cpu": [ "riscv64" ], @@ -816,9 +816,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.59.0.tgz", - "integrity": "sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==", + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.0.tgz", + "integrity": "sha512-3ObQs0BhvPgiUVZrN7gqCSvmFuMWvWvsjG5ayJ3Lraqv+2KhOsp+pUbigqbeWqueGIsnn+09HBw27rJ+gYK4VQ==", "cpu": [ "s390x" ], @@ -830,9 +830,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.59.0.tgz", - "integrity": "sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==", + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.0.tgz", + "integrity": "sha512-EtylprDtQPdS5rXvAayrNDYoJhIz1/vzN2fEubo3yLE7tfAw+948dO0g4M0vkTVFhKojnF+n6C8bDNe+gDRdTg==", "cpu": [ "x64" ], @@ -844,9 +844,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.59.0.tgz", - "integrity": "sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==", + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.0.tgz", + "integrity": "sha512-k09oiRCi/bHU9UVFqD17r3eJR9bn03TyKraCrlz5ULFJGdJGi7VOmm9jl44vOJvRJ6P7WuBi/s2A97LxxHGIdw==", "cpu": [ "x64" ], @@ -858,9 +858,9 @@ ] }, "node_modules/@rollup/rollup-openbsd-x64": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.59.0.tgz", - "integrity": "sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==", + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.0.tgz", + "integrity": "sha512-1o/0/pIhozoSaDJoDcec+IVLbnRtQmHwPV730+AOD29lHEEo4F5BEUB24H0OBdhbBBDwIOSuf7vgg0Ywxdfiiw==", "cpu": [ "x64" ], @@ -872,9 +872,9 @@ ] }, "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.59.0.tgz", - "integrity": "sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==", + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.0.tgz", + "integrity": "sha512-pESDkos/PDzYwtyzB5p/UoNU/8fJo68vcXM9ZW2V0kjYayj1KaaUfi1NmTUTUpMn4UhU4gTuK8gIaFO4UGuMbA==", "cpu": [ "arm64" ], @@ -886,9 +886,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.59.0.tgz", - "integrity": "sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==", + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.0.tgz", + "integrity": "sha512-hj1wFStD7B1YBeYmvY+lWXZ7ey73YGPcViMShYikqKT1GtstIKQAtfUI6yrzPjAy/O7pO0VLXGmUVWXQMaYgTQ==", "cpu": [ "arm64" ], @@ -900,9 +900,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.59.0.tgz", - "integrity": "sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==", + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.0.tgz", + "integrity": "sha512-SyaIPFoxmUPlNDq5EHkTbiKzmSEmq/gOYFI/3HHJ8iS/v1mbugVa7dXUzcJGQfoytp9DJFLhHH4U3/eTy2Bq4w==", "cpu": [ "ia32" ], @@ -914,9 +914,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.59.0.tgz", - "integrity": "sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==", + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.0.tgz", + "integrity": "sha512-RdcryEfzZr+lAr5kRm2ucN9aVlCCa2QNq4hXelZxb8GG0NJSazq44Z3PCCc8wISRuCVnGs0lQJVX5Vp6fKA+IA==", "cpu": [ "x64" ], @@ -928,9 +928,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.59.0.tgz", - "integrity": "sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==", + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.0.tgz", + "integrity": "sha512-PrsWNQ8BuE00O3Xsx3ALh2Df8fAj9+cvvX9AIA6o4KpATR98c9mud4XtDWVvsEuyia5U4tVSTKygawyJkjm60w==", "cpu": [ "x64" ], @@ -1083,9 +1083,9 @@ "license": "MIT" }, "node_modules/baseline-browser-mapping": { - "version": "2.10.8", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.8.tgz", - "integrity": "sha512-PCLz/LXGBsNTErbtB6i5u4eLpHeMfi93aUv5duMmj6caNu6IphS4q6UevDnL36sZQv9lrP11dbPKGMaXPwMKfQ==", + "version": "2.10.10", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.10.tgz", + "integrity": "sha512-sUoJ3IMxx4AyRqO4MLeHlnGDkyXRoUG0/AI9fjK+vS72ekpV0yWVY7O0BVjmBcRtkNcsAO2QDZ4tdKKGoI6YaQ==", "dev": true, "license": "Apache-2.0", "bin": { @@ -1203,9 +1203,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001780", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001780.tgz", - "integrity": "sha512-llngX0E7nQci5BPJDqoZSbuZ5Bcs9F5db7EtgfwBerX9XGtkkiO4NwfDDIRzHTTwcYC8vC7bmeUEPGrKlR/TkQ==", + "version": "1.0.30001781", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001781.tgz", + "integrity": "sha512-RdwNCyMsNBftLjW6w01z8bKEvT6e/5tpPVEgtn22TiLGlstHOVecsX2KHFkD5e/vRnIE4EGzpuIODb3mtswtkw==", "dev": true, "funding": [ { @@ -3382,9 +3382,9 @@ } }, "node_modules/rollup": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.59.0.tgz", - "integrity": "sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==", + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.0.tgz", + "integrity": "sha512-yqjxruMGBQJ2gG4HtjZtAfXArHomazDHoFwFFmZZl0r7Pdo7qCIXKqKHZc8yeoMgzJJ+pO6pEEHa+V7uzWlrAQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3398,31 +3398,31 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.59.0", - "@rollup/rollup-android-arm64": "4.59.0", - "@rollup/rollup-darwin-arm64": "4.59.0", - "@rollup/rollup-darwin-x64": "4.59.0", - "@rollup/rollup-freebsd-arm64": "4.59.0", - "@rollup/rollup-freebsd-x64": "4.59.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.59.0", - "@rollup/rollup-linux-arm-musleabihf": "4.59.0", - "@rollup/rollup-linux-arm64-gnu": "4.59.0", - "@rollup/rollup-linux-arm64-musl": "4.59.0", - "@rollup/rollup-linux-loong64-gnu": "4.59.0", - "@rollup/rollup-linux-loong64-musl": "4.59.0", - "@rollup/rollup-linux-ppc64-gnu": "4.59.0", - "@rollup/rollup-linux-ppc64-musl": "4.59.0", - "@rollup/rollup-linux-riscv64-gnu": "4.59.0", - "@rollup/rollup-linux-riscv64-musl": "4.59.0", - "@rollup/rollup-linux-s390x-gnu": "4.59.0", - "@rollup/rollup-linux-x64-gnu": "4.59.0", - "@rollup/rollup-linux-x64-musl": "4.59.0", - "@rollup/rollup-openbsd-x64": "4.59.0", - "@rollup/rollup-openharmony-arm64": "4.59.0", - "@rollup/rollup-win32-arm64-msvc": "4.59.0", - "@rollup/rollup-win32-ia32-msvc": "4.59.0", - "@rollup/rollup-win32-x64-gnu": "4.59.0", - "@rollup/rollup-win32-x64-msvc": "4.59.0", + "@rollup/rollup-android-arm-eabi": "4.60.0", + "@rollup/rollup-android-arm64": "4.60.0", + "@rollup/rollup-darwin-arm64": "4.60.0", + "@rollup/rollup-darwin-x64": "4.60.0", + "@rollup/rollup-freebsd-arm64": "4.60.0", + "@rollup/rollup-freebsd-x64": "4.60.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.60.0", + "@rollup/rollup-linux-arm-musleabihf": "4.60.0", + "@rollup/rollup-linux-arm64-gnu": "4.60.0", + "@rollup/rollup-linux-arm64-musl": "4.60.0", + "@rollup/rollup-linux-loong64-gnu": "4.60.0", + "@rollup/rollup-linux-loong64-musl": "4.60.0", + "@rollup/rollup-linux-ppc64-gnu": "4.60.0", + "@rollup/rollup-linux-ppc64-musl": "4.60.0", + "@rollup/rollup-linux-riscv64-gnu": "4.60.0", + "@rollup/rollup-linux-riscv64-musl": "4.60.0", + "@rollup/rollup-linux-s390x-gnu": "4.60.0", + "@rollup/rollup-linux-x64-gnu": "4.60.0", + "@rollup/rollup-linux-x64-musl": "4.60.0", + "@rollup/rollup-openbsd-x64": "4.60.0", + "@rollup/rollup-openharmony-arm64": "4.60.0", + "@rollup/rollup-win32-arm64-msvc": "4.60.0", + "@rollup/rollup-win32-ia32-msvc": "4.60.0", + "@rollup/rollup-win32-x64-gnu": "4.60.0", + "@rollup/rollup-win32-x64-msvc": "4.60.0", "fsevents": "~2.3.2" } }, diff --git a/tests/Feature/Api/AuthenticatedApiTest.php b/tests/Feature/Api/AuthenticatedApiTest.php index 01f01a65..df269faa 100644 --- a/tests/Feature/Api/AuthenticatedApiTest.php +++ b/tests/Feature/Api/AuthenticatedApiTest.php @@ -188,8 +188,15 @@ public function test_get_instance_status(): void $response = $this->getJson("/api/instance/{$instance->uuid}/status"); $response->assertOk(); + $this->assertEquals($instance->uuid, $response->json('data.uuid')); + $this->assertEquals($instance->name, $response->json('data.name')); $this->assertEquals($instance->status->value, $response->json('data.status')); $this->assertEquals('Creating...', $response->json('data.status_message')); + $this->assertNull($response->json('data.app_url')); + $this->assertEquals($this->storeApp->uuid, $response->json('data.store_app.uuid')); + $this->assertEquals($this->storeApp->name, $response->json('data.store_app.name')); + $this->assertEquals('git@github.com:example/repo.git', $response->json('data.store_app.git_url')); + $this->assertNotNull($response->json('data.created_at')); $this->assertEquals('/app/.lagoon/scripts/polydock_claim.sh', $response->json('data.lagoon_claim_script')); $this->assertEquals('test-lagoon-name', $response->json('data.lagoon_project_name')); } diff --git a/tests/Feature/Filament/Admin/Resources/UserGroupResource/RelationManagers/UsersRelationManagerTest.php b/tests/Feature/Filament/Admin/Resources/UserGroupResource/RelationManagers/UsersRelationManagerTest.php new file mode 100644 index 00000000..bddd084f --- /dev/null +++ b/tests/Feature/Filament/Admin/Resources/UserGroupResource/RelationManagers/UsersRelationManagerTest.php @@ -0,0 +1,142 @@ +admin = User::factory()->create([ + 'email' => 'admin@example.com', + ]); + + // Create a user group + $this->userGroup = UserGroup::factory()->create([ + 'name' => 'Test Group', + ]); + + // Set the admin panel as current for Filament + Filament::setCurrentPanel(Filament::getPanel('admin')); + } + + public function test_can_attach_existing_user_to_group(): void + { + $this->actingAs($this->admin); + + $existingUser = User::factory()->create([ + 'email' => 'existing@example.com', + ]); + + Livewire::test(UsersRelationManager::class, [ + 'ownerRecord' => $this->userGroup, + 'pageClass' => EditUserGroup::class, + ]) + ->callTableAction('add_user', data: [ + 'user_id' => $existingUser->id, + 'role' => UserGroupRoleEnum::MEMBER->value, + 'mode' => 'existing', + ]) + ->assertHasNoTableActionErrors(); + + // Verify user is attached to the group with the correct role + $this->assertTrue($this->userGroup->users()->where('users.id', $existingUser->id)->exists()); + $this->assertEquals( + UserGroupRoleEnum::MEMBER->value, + $this->userGroup->users()->where('users.id', $existingUser->id)->first()->pivot->role + ); + } + + public function test_can_create_and_attach_new_user_to_group(): void + { + $this->actingAs($this->admin); + + Livewire::test(UsersRelationManager::class, [ + 'ownerRecord' => $this->userGroup, + 'pageClass' => EditUserGroup::class, + ]) + ->callTableAction('add_user', data: [ + 'mode' => 'new', + 'first_name' => 'John', + 'last_name' => 'Doe', + 'email' => 'newuser@example.com', + 'password' => 'password123', + 'role' => UserGroupRoleEnum::OWNER->value, + ]) + ->assertHasNoTableActionErrors(); + + // Verify user was created in the database + $newUser = User::where('email', 'newuser@example.com')->first(); + $this->assertNotNull($newUser); + $this->assertEquals('John', $newUser->first_name); + $this->assertEquals('Doe', $newUser->last_name); + + // Verify user is attached to the group with the correct role + $this->assertTrue($this->userGroup->users()->where('users.id', $newUser->id)->exists()); + $this->assertEquals( + UserGroupRoleEnum::OWNER->value, + $this->userGroup->users()->where('users.id', $newUser->id)->first()->pivot->role + ); + } + + public function test_add_user_form_validates_required_fields_when_creating_new(): void + { + $this->actingAs($this->admin); + + Livewire::test(UsersRelationManager::class, [ + 'ownerRecord' => $this->userGroup, + 'pageClass' => EditUserGroup::class, + ]) + ->callTableAction('add_user', data: [ + 'mode' => 'new', + 'first_name' => '', + 'last_name' => '', + 'email' => '', + 'password' => '', + 'role' => null, + ]) + ->assertHasTableActionErrors([ + 'first_name' => 'required', + 'last_name' => 'required', + 'email' => 'required', + 'password' => 'required', + 'role' => 'required', + ]); + } + + public function test_add_user_form_validates_required_user_id_when_attaching_existing(): void + { + $this->actingAs($this->admin); + + Livewire::test(UsersRelationManager::class, [ + 'ownerRecord' => $this->userGroup, + 'pageClass' => EditUserGroup::class, + ]) + ->callTableAction('add_user', data: [ + 'mode' => 'existing', + 'user_id' => null, + 'role' => UserGroupRoleEnum::MEMBER->value, + ]) + ->assertHasTableActionErrors([ + 'user_id' => 'required', + ]); + } +}