diff --git a/app/Actions/Projects/CreateProject.php b/app/Actions/Projects/CreateProject.php index 45c4137..060747f 100644 --- a/app/Actions/Projects/CreateProject.php +++ b/app/Actions/Projects/CreateProject.php @@ -14,7 +14,7 @@ public function create(User $user, array $input): Project $input['name'] = strtolower($input['name']); } - $this->validate($user, $input); + $this->validate($input); $project = new Project([ 'name' => $input['name'], @@ -22,10 +22,12 @@ public function create(User $user, array $input): Project $project->save(); + $project->users()->attach($user); + return $project; } - private function validate(User $user, array $input): void + private function validate(array $input): void { Validator::make($input, [ 'name' => [ diff --git a/app/Actions/SourceControl/ConnectSourceControl.php b/app/Actions/SourceControl/ConnectSourceControl.php index da902db..667559e 100644 --- a/app/Actions/SourceControl/ConnectSourceControl.php +++ b/app/Actions/SourceControl/ConnectSourceControl.php @@ -3,6 +3,7 @@ namespace App\Actions\SourceControl; use App\Models\SourceControl; +use App\Models\User; use Illuminate\Support\Arr; use Illuminate\Support\Facades\Validator; use Illuminate\Validation\Rule; @@ -10,7 +11,7 @@ class ConnectSourceControl { - public function connect(array $input): void + public function connect(User $user, array $input): void { $this->validate($input); @@ -18,6 +19,7 @@ public function connect(array $input): void 'provider' => $input['provider'], 'profile' => $input['name'], 'url' => Arr::has($input, 'url') ? $input['url'] : null, + 'project_id' => isset($input['global']) && $input['global'] ? null : $user->current_project_id, ]); $this->validateProvider($sourceControl, $input); diff --git a/app/Actions/SourceControl/EditSourceControl.php b/app/Actions/SourceControl/EditSourceControl.php new file mode 100644 index 0000000..a7665e5 --- /dev/null +++ b/app/Actions/SourceControl/EditSourceControl.php @@ -0,0 +1,54 @@ +validate($input); + + $sourceControl->profile = $input['name']; + $sourceControl->url = isset($input['url']) ? $input['url'] : null; + $sourceControl->project_id = isset($input['global']) && $input['global'] ? null : $user->current_project_id; + + $this->validateProvider($sourceControl, $input); + + $sourceControl->provider_data = $sourceControl->provider()->createData($input); + + if (! $sourceControl->provider()->connect()) { + throw ValidationException::withMessages([ + 'token' => __('Cannot connect to :provider or invalid token!', ['provider' => $sourceControl->provider] + ), + ]); + } + + $sourceControl->save(); + } + + /** + * @throws ValidationException + */ + private function validate(array $input): void + { + $rules = [ + 'name' => [ + 'required', + ], + ]; + Validator::make($input, $rules)->validate(); + } + + /** + * @throws ValidationException + */ + private function validateProvider(SourceControl $sourceControl, array $input): void + { + Validator::make($input, $sourceControl->provider()->createRules($input))->validate(); + } +} diff --git a/app/Http/Controllers/Settings/SourceControlController.php b/app/Http/Controllers/Settings/SourceControlController.php index 019b20f..55a9ea9 100644 --- a/app/Http/Controllers/Settings/SourceControlController.php +++ b/app/Http/Controllers/Settings/SourceControlController.php @@ -4,6 +4,7 @@ use App\Actions\SourceControl\ConnectSourceControl; use App\Actions\SourceControl\DeleteSourceControl; +use App\Actions\SourceControl\EditSourceControl; use App\Facades\Toast; use App\Helpers\HtmxResponse; use App\Http\Controllers\Controller; @@ -14,16 +15,23 @@ class SourceControlController extends Controller { - public function index(): View + public function index(Request $request): View { - return view('settings.source-controls.index', [ - 'sourceControls' => SourceControl::query()->orderByDesc('id')->get(), - ]); + $data = [ + 'sourceControls' => SourceControl::getByCurrentProject(), + ]; + + if ($request->has('edit')) { + $data['editSourceControl'] = SourceControl::find($request->input('edit')); + } + + return view('settings.source-controls.index', $data); } public function connect(Request $request): HtmxResponse { app(ConnectSourceControl::class)->connect( + $request->user(), $request->input(), ); @@ -32,6 +40,19 @@ public function connect(Request $request): HtmxResponse return htmx()->redirect(route('settings.source-controls')); } + public function update(SourceControl $sourceControl, Request $request): HtmxResponse + { + app(EditSourceControl::class)->edit( + $sourceControl, + $request->user(), + $request->input(), + ); + + Toast::success('Source control updated.'); + + return htmx()->redirect(route('settings.source-controls')); + } + public function delete(SourceControl $sourceControl): RedirectResponse { try { diff --git a/app/Models/Project.php b/app/Models/Project.php index d354817..8226f2a 100644 --- a/app/Models/Project.php +++ b/app/Models/Project.php @@ -19,6 +19,7 @@ * @property User $user * @property Collection $servers * @property Collection $notificationChannels + * @property Collection $sourceControls */ class Project extends Model { @@ -59,4 +60,9 @@ public function users(): BelongsToMany { return $this->belongsToMany(User::class, 'user_project')->withTimestamps(); } + + public function sourceControls(): HasMany + { + return $this->hasMany(SourceControl::class); + } } diff --git a/app/Models/SourceControl.php b/app/Models/SourceControl.php index 93c04f6..8f4c4c7 100755 --- a/app/Models/SourceControl.php +++ b/app/Models/SourceControl.php @@ -3,7 +3,9 @@ namespace App\Models; use App\SourceControlProviders\SourceControlProvider; +use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\Factories\HasFactory; +use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\HasMany; /** @@ -12,6 +14,7 @@ * @property ?string $profile * @property ?string $url * @property string $access_token + * @property ?int $project_id */ class SourceControl extends AbstractModel { @@ -23,11 +26,13 @@ class SourceControl extends AbstractModel 'profile', 'url', 'access_token', + 'project_id', ]; protected $casts = [ 'access_token' => 'encrypted', 'provider_data' => 'encrypted:array', + 'project_id' => 'integer', ]; public function provider(): SourceControlProvider @@ -46,4 +51,16 @@ public function sites(): HasMany { return $this->hasMany(Site::class); } + + public function project(): BelongsTo + { + return $this->belongsTo(Project::class); + } + + public static function getByCurrentProject(): Collection + { + return self::query() + ->where('project_id', auth()->user()->current_project_id) + ->orWhereNull('project_id')->get(); + } } diff --git a/database/migrations/2024_05_07_184201_add_project_id_to_source_controls_table.php b/database/migrations/2024_05_07_184201_add_project_id_to_source_controls_table.php new file mode 100644 index 0000000..58fbaab --- /dev/null +++ b/database/migrations/2024_05_07_184201_add_project_id_to_source_controls_table.php @@ -0,0 +1,28 @@ +unsignedBigInteger('project_id')->nullable(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('source_controls', function (Blueprint $table) { + $table->dropColumn('project_id'); + }); + } +}; diff --git a/resources/views/components/checkbox.blade.php b/resources/views/components/checkbox.blade.php new file mode 100644 index 0000000..1ed43ad --- /dev/null +++ b/resources/views/components/checkbox.blade.php @@ -0,0 +1,19 @@ +@props([ + "disabled" => false, + "id", + "name", + "value", +]) + +
+ merge(["disabled" => $disabled, "class" => "rounded border-gray-300 text-indigo-600 shadow-sm focus:ring-indigo-500 dark:border-gray-700 dark:bg-gray-900 dark:focus:ring-indigo-600 dark:focus:ring-offset-gray-800"]) }} + /> + +
diff --git a/resources/views/components/modal.blade.php b/resources/views/components/modal.blade.php index d82b8be..ea936ce 100644 --- a/resources/views/components/modal.blade.php +++ b/resources/views/components/modal.blade.php @@ -18,7 +18,8 @@
+
+ + Is Global (Accessible in all projects) + + @error("global") + + @enderror +
+
{{ __("Cancel") }} diff --git a/resources/views/settings/source-controls/partials/edit-source-control.blade.php b/resources/views/settings/source-controls/partials/edit-source-control.blade.php new file mode 100644 index 0000000..427144c --- /dev/null +++ b/resources/views/settings/source-controls/partials/edit-source-control.blade.php @@ -0,0 +1,130 @@ + +
$sourceControl->id]) }}" + hx-swap="outerHTML" + hx-select="#edit-source-control-form" + hx-ext="disable-element" + hx-disable-element="#btn-edit-source-control" + class="p-6" + > + @csrf + +

+ {{ __("Edit Source Control") }} +

+ +
+ + + @error("name") + + @enderror +
+ + @if ($sourceControl->provider == \App\Enums\SourceControl::GITLAB) +
+ + + + If you run a self-managed gitlab enter the url here, leave empty to use gitlab.com + + @error("url") + + @enderror +
+ @endif + + @if (in_array($sourceControl->provider, [\App\Enums\SourceControl::GITLAB, \App\Enums\SourceControl::GITHUB])) +
+ + + @error("token") + + @enderror +
+ @endif + + @if ($sourceControl->provider == \App\Enums\SourceControl::BITBUCKET) +
+
+ + + Your Bitbucket username + @error("username") + + @enderror +
+ +
+ + + + Create a new + + App Password + + in your Bitbucket account with write and admin access to Workspaces, Projects, Repositories and + Webhooks + + @error("password") + + @enderror +
+
+ @endif + +
+ + Is Global (Accessible in all projects) + + @error("global") + + @enderror +
+ +
+ + {{ __("Cancel") }} + + + + {{ __("Save") }} + +
+
+
diff --git a/resources/views/settings/source-controls/partials/source-controls-list.blade.php b/resources/views/settings/source-controls/partials/source-controls-list.blade.php index 2c2a9e9..a879d34 100644 --- a/resources/views/settings/source-controls/partials/source-controls-list.blade.php +++ b/resources/views/settings/source-controls/partials/source-controls-list.blade.php @@ -14,13 +14,26 @@ @include("settings.source-controls.partials.icons." . $sourceControl->provider . "-icon")
- {{ $sourceControl->profile }} +
+ {{ $sourceControl->profile }} + @if (! $sourceControl->project_id) + GLOBAL + @endif +
+ + + @@ -32,6 +45,12 @@ @endforeach @include("settings.source-controls.partials.delete-source-control") + +
+ @if (isset($editSourceControl)) + @include("settings.source-controls.partials.edit-source-control", ["sourceControl" => $editSourceControl]) + @endif +
@else
diff --git a/resources/views/sites/partials/create/fields/source-control.blade.php b/resources/views/sites/partials/create/fields/source-control.blade.php index 515d557..313f432 100644 --- a/resources/views/sites/partials/create/fields/source-control.blade.php +++ b/resources/views/sites/partials/create/fields/source-control.blade.php @@ -3,7 +3,7 @@
- @foreach ($sourceControls as $sourceControl) + @foreach (\App\Models\SourceControl::getByCurrentProject() as $sourceControl)