-
Notifications
You must be signed in to change notification settings - Fork 0
Add VCS instances functionality #10
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace App\Http\Controllers; | ||
|
|
||
| use App\Enums\VcsPlatform; | ||
| use App\Http\Requests\VcsInstances\VcsInstanceCreateRequest; | ||
| use App\Models\VcsInstance; | ||
| use Illuminate\Http\RedirectResponse; | ||
| use Inertia\Inertia; | ||
| use Inertia\Response; | ||
|
|
||
| class VcsInstanceController extends Controller | ||
| { | ||
| public function create(): Response | ||
| { | ||
| return Inertia::render('vcsInstances/Create', [ | ||
| 'platforms' => VcsPlatform::getLabels(), | ||
| ]); | ||
| } | ||
|
|
||
| public function store(VcsInstanceCreateRequest $request): RedirectResponse | ||
| { | ||
| $vcsInstance = new VcsInstance($request->validated()); | ||
| $vcsInstance->installation_id = $vcsInstance->platform === VcsPlatform::GITHUB ? $vcsInstance->installation_id : null; | ||
| $vcsInstance->token = $vcsInstance->platform === VcsPlatform::GITLAB ? $vcsInstance->token : null; | ||
| $vcsInstance->save(); | ||
|
|
||
| return to_route('repositories.index'); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace App\Http\Requests\VcsInstances; | ||
|
|
||
| use App\Models\VcsInstance; | ||
| use Illuminate\Foundation\Http\FormRequest; | ||
|
|
||
| class VcsInstanceCreateRequest extends FormRequest | ||
| { | ||
| /** | ||
| * Get the validation rules that apply to the request. | ||
| * | ||
| * @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string> | ||
| */ | ||
| public function rules(): array | ||
| { | ||
| return VcsInstance::rules(); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -10,6 +10,7 @@ | |||||
| use Illuminate\Database\Eloquent\Factories\HasFactory; | ||||||
| use Illuminate\Database\Eloquent\Model; | ||||||
| use Illuminate\Database\Eloquent\Relations\HasMany; | ||||||
| use Illuminate\Validation\Rule; | ||||||
|
|
||||||
| /** | ||||||
| * Attributes | ||||||
|
|
@@ -30,6 +31,14 @@ class VcsInstance extends Model | |||||
|
|
||||||
| public $timestamps = false; | ||||||
|
|
||||||
| protected $fillable = [ | ||||||
| 'name', | ||||||
| 'api_url', | ||||||
| 'token', | ||||||
| 'installation_id', | ||||||
| 'platform', | ||||||
| ]; | ||||||
|
|
||||||
| protected function casts(): array | ||||||
| { | ||||||
| return [ | ||||||
|
|
@@ -38,6 +47,20 @@ protected function casts(): array | |||||
| ]; | ||||||
| } | ||||||
|
|
||||||
| /** | ||||||
| * @return array<string, array<array-key, mixed>> | ||||||
| */ | ||||||
| public static function rules(): array | ||||||
| { | ||||||
| return [ | ||||||
| 'name' => ['required', 'string', 'max:255'], | ||||||
| 'api_url' => ['required', 'url', 'max:255'], | ||||||
| 'token' => ['required_if:platform,gitlab', 'nullable', 'string'], | ||||||
| 'installation_id' => ['required_if:platform,github', 'nullable', 'string', 'max:255'], | ||||||
|
||||||
| 'installation_id' => ['required_if:platform,github', 'nullable', 'string', 'max:255'], | |
| 'installation_id' => ['required_if:platform,github', 'nullable', 'integer', 'min:1', 'max:255'], |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,109 @@ | ||
| <script setup lang="ts"> | ||
| import Heading from '@/components/Heading.vue'; | ||
| import InputError from '@/components/InputError.vue'; | ||
| import { Button } from '@/components/ui/button'; | ||
| import { Input } from '@/components/ui/input'; | ||
| import { Label } from '@/components/ui/label'; | ||
| import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; | ||
| import AppLayout from '@/layouts/AppLayout.vue'; | ||
| import type { BreadcrumbItem, VcsInstance } from '@/types'; | ||
| import { Head, useForm } from '@inertiajs/vue3'; | ||
|
|
||
| const breadcrumbs: BreadcrumbItem[] = [ | ||
| { | ||
| title: 'Repositories', | ||
| href: '/repositories', | ||
| }, | ||
| { | ||
| title: 'Add VCS instance', | ||
| href: '#', | ||
| }, | ||
| ]; | ||
|
|
||
| interface Props { | ||
| platforms: Record<string, string>; | ||
| } | ||
|
|
||
| defineProps<Props>(); | ||
|
|
||
| const form = useForm<VcsInstance>({ | ||
| name: '', | ||
| api_url: '', | ||
| token: '', | ||
| installation_id: '', | ||
| platform: 'gitlab', | ||
| }); | ||
|
|
||
| const createVcsInstance = () => { | ||
| form.post(route('vcs-instances.store'), { | ||
| preserveScroll: true, | ||
| }); | ||
| }; | ||
| </script> | ||
|
|
||
| <template> | ||
| <AppLayout :breadcrumbs="breadcrumbs"> | ||
| <Head title="Add VCS instance" /> | ||
|
|
||
| <div class="flex p-4"> | ||
| <div class="mx-auto w-full py-10 sm:w-1/2"> | ||
| <Heading title="Add VCS instance" /> | ||
| <form @submit.prevent="createVcsInstance" class="space-y-6"> | ||
| <div class="grid gap-2"> | ||
| <Label for="name">Name</Label> | ||
| <Input id="name" v-model="form.name" class="mt-1 block w-full" required /> | ||
| <InputError :message="form.errors.name" /> | ||
| </div> | ||
|
|
||
| <div class="grid gap-2"> | ||
| <Label for="api_url">API URL</Label> | ||
| <Input id="api_url" v-model="form.api_url" class="mt-1 block w-full" required /> | ||
| <InputError :message="form.errors.api_url" /> | ||
| <p class="text-sm text-muted-foreground">Base API URL e.g. https://api.github.com/ or https://gitlab.com/api/</p> | ||
| </div> | ||
|
|
||
| <div class="grid gap-2"> | ||
| <Label for="platform">Platform</Label> | ||
| <Select id="platform" v-model="form.platform" class="mt-1 block w-full" required> | ||
| <SelectTrigger> | ||
| <SelectValue placeholder="Select platform" /> | ||
| </SelectTrigger> | ||
| <SelectContent> | ||
| <SelectItem v-for="(label, value) in platforms" :key="value" :value="value">{{ label }}</SelectItem> | ||
| </SelectContent> | ||
| </Select> | ||
| <InputError :message="form.errors.platform" /> | ||
| </div> | ||
|
|
||
| <div class="grid gap-2" v-if="form.platform === 'github'"> | ||
| <Label for="installation_id">Installation ID</Label> | ||
| <Input id="installation_id" v-model="form.installation_id" class="mt-1 block w-full" required /> | ||
| <InputError :message="form.errors.installation_id" /> | ||
| <p class="text-sm text-muted-foreground"> | ||
| Installation ID could be found at Settings → Integrations → Applications → App as part of the URL | ||
| </p> | ||
| </div> | ||
|
|
||
| <div class="grid gap-2" v-if="form.platform === 'gitlab'"> | ||
| <Label for="token">Token</Label> | ||
| <Input id="token" type="password" v-model="form.token" class="mt-1 block w-full" required /> | ||
| <InputError :message="form.errors.token" /> | ||
| </div> | ||
|
|
||
| <div class="flex items-center gap-4"> | ||
| <Button :disabled="form.processing">Create VCS instance</Button> | ||
|
|
||
| <Transition | ||
| enter-active-class="transition ease-in-out" | ||
| enter-from-class="opacity-0" | ||
| leave-active-class="transition ease-in-out" | ||
| leave-to-class="opacity-0" | ||
| > | ||
| <p v-show="form.recentlySuccessful" class="text-sm text-neutral-600">Saved.</p> | ||
| </Transition> | ||
| </div> | ||
| </form> | ||
| </div> | ||
| </div> | ||
| </AppLayout> | ||
| </template> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
api_urlis later used to build outbound HTTP requests (e.g., GitHub/GitLab API calls). Validating with the genericurlrule allows non-HTTP(S) schemes and potentially internal/loopback/private-network hosts, which can lead to misconfiguration and SSRF risk. Consider restricting to HTTPS and (if feasible) rejecting localhost/private IP ranges / non-public hosts at validation time.