added jquery and also fixed fontawsome
This commit is contained in:
@@ -0,0 +1,106 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Modules\Helpdesk\Http\Controllers;
|
||||
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use JsonException;
|
||||
use Modules\Helpdesk\Models\GitRepository;
|
||||
use Modules\Helpdesk\Services\GitWebhookIngestionService;
|
||||
|
||||
final class GitWebhookController
|
||||
{
|
||||
/**
|
||||
* Ingest a signed Git provider webhook for a registered Helpdesk repository.
|
||||
*/
|
||||
public function __invoke(Request $request, GitRepository $repository, GitWebhookIngestionService $service): JsonResponse
|
||||
{
|
||||
$provider = strtolower((string) ($repository->provider ?: 'git'));
|
||||
$event = $this->eventName($request, $provider);
|
||||
$deliveryId = $this->deliveryId($request, $provider);
|
||||
$rawPayload = $request->getContent();
|
||||
|
||||
if (! $repository->is_active) {
|
||||
$result = ['status' => 'rejected', 'message' => 'Repository is inactive.', 'commit_count' => 0, 'linked_count' => 0, 'ignored_count' => 1];
|
||||
$service->logWebhook($repository, $provider, $deliveryId, $event, $result);
|
||||
|
||||
return response()->json($result, 403);
|
||||
}
|
||||
|
||||
if (! $this->hasValidSignature($request, $repository, $provider, $rawPayload)) {
|
||||
$result = ['status' => 'rejected', 'message' => 'Git webhook signature or token validation failed.', 'commit_count' => 0, 'linked_count' => 0, 'ignored_count' => 1];
|
||||
$service->logWebhook($repository, $provider, $deliveryId, $event, $result);
|
||||
|
||||
return response()->json($result, 403);
|
||||
}
|
||||
|
||||
try {
|
||||
/** @var array<string, mixed> $payload */
|
||||
$payload = json_decode($rawPayload, true, 512, JSON_THROW_ON_ERROR);
|
||||
} catch (JsonException) {
|
||||
$result = ['status' => 'rejected', 'message' => 'Invalid JSON webhook payload.', 'commit_count' => 0, 'linked_count' => 0, 'ignored_count' => 1];
|
||||
$service->logWebhook($repository, $provider, $deliveryId, $event, $result);
|
||||
|
||||
return response()->json($result, 400);
|
||||
}
|
||||
|
||||
$result = $service->ingest($repository, $payload, $provider, $deliveryId, $event);
|
||||
|
||||
return response()->json($result, 202);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate provider-specific webhook signatures or tokens.
|
||||
*/
|
||||
private function hasValidSignature(Request $request, GitRepository $repository, string $provider, string $rawPayload): bool
|
||||
{
|
||||
$secret = trim((string) $repository->webhook_secret);
|
||||
|
||||
if ($secret === '') {
|
||||
return false;
|
||||
}
|
||||
|
||||
return match ($provider) {
|
||||
'github' => $this->hasValidGitHubSignature($request, $secret, $rawPayload),
|
||||
'gitlab' => hash_equals($secret, (string) $request->header('X-Gitlab-Token', '')),
|
||||
default => hash_equals($secret, (string) $request->header('X-Helpdesk-Token', '')),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate GitHub's X-Hub-Signature-256 HMAC header.
|
||||
*/
|
||||
private function hasValidGitHubSignature(Request $request, string $secret, string $rawPayload): bool
|
||||
{
|
||||
$signature = (string) $request->header('X-Hub-Signature-256', '');
|
||||
$expected = 'sha256='.hash_hmac('sha256', $rawPayload, $secret);
|
||||
|
||||
return hash_equals($expected, $signature);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the event name from provider-specific headers.
|
||||
*/
|
||||
private function eventName(Request $request, string $provider): ?string
|
||||
{
|
||||
return match ($provider) {
|
||||
'github' => $request->header('X-GitHub-Event'),
|
||||
'gitlab' => $request->header('X-Gitlab-Event'),
|
||||
default => $request->header('X-Helpdesk-Event', 'push'),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the provider delivery/request id used for idempotent audit tracing.
|
||||
*/
|
||||
private function deliveryId(Request $request, string $provider): ?string
|
||||
{
|
||||
return match ($provider) {
|
||||
'github' => $request->header('X-GitHub-Delivery'),
|
||||
'gitlab' => $request->header('X-Gitlab-Event-UUID') ?: $request->header('X-Request-Id'),
|
||||
default => $request->header('X-Helpdesk-Delivery'),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Modules\Helpdesk\Http\Controllers;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Illuminate\Support\HtmlString;
|
||||
|
||||
final class HelpdeskController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display the Helpdesk module landing page inside the application shell.
|
||||
*/
|
||||
public function __invoke(): View
|
||||
{
|
||||
return view('layouts.app', [
|
||||
'title' => __('Helpdesk'),
|
||||
'slot' => new HtmlString(view('helpdesk::index')->render()),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Modules\Helpdesk\Http\Controllers;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Validation\Rule;
|
||||
use Modules\Helpdesk\Services\TicketService;
|
||||
|
||||
final class TicketStoreController extends Controller
|
||||
{
|
||||
/**
|
||||
* Store a new helpdesk ticket using a normal HTTP form post.
|
||||
*
|
||||
* This avoids the Livewire update endpoint for ticket creation, which prevents
|
||||
* proxy/hash update-route 404s from blocking ticket creation.
|
||||
*/
|
||||
public function __invoke(Request $request, TicketService $tickets): RedirectResponse
|
||||
{
|
||||
$maxUploadKilobytes = (int) config('helpdesk.max_upload_kb', 10240);
|
||||
$allowedMimes = (array) config('helpdesk.allowed_mimes', ['jpg', 'jpeg', 'png', 'pdf', 'txt', 'doc', 'docx', 'xls', 'xlsx']);
|
||||
|
||||
$validated = $request->validate([
|
||||
'company_id' => ['nullable', 'integer'],
|
||||
'subject' => ['required', 'string', 'max:200'],
|
||||
'body' => ['required', 'string', 'min:10'],
|
||||
'priority_id' => ['required', 'integer', Rule::exists('helpdesk_priorities', 'id')],
|
||||
'category_id' => ['nullable', 'integer', Rule::exists('helpdesk_categories', 'id')],
|
||||
'project_id' => ['nullable', 'integer', Rule::exists('helpdesk_projects', 'id')],
|
||||
'product_id' => ['nullable', 'integer', Rule::exists('helpdesk_products', 'id')],
|
||||
'team_id' => ['nullable', 'integer', Rule::exists('helpdesk_teams', 'id')],
|
||||
'due_at' => ['nullable', 'date'],
|
||||
'attachments' => ['nullable', 'array'],
|
||||
'attachments.*' => ['file', 'max:'.$maxUploadKilobytes, 'mimes:'.implode(',', $allowedMimes)],
|
||||
]);
|
||||
|
||||
$user = Auth::user();
|
||||
|
||||
abort_if($user === null, 403);
|
||||
|
||||
$uploadedFiles = $request->file('attachments', []);
|
||||
$uploadedFiles = is_array($uploadedFiles) ? $uploadedFiles : [$uploadedFiles];
|
||||
|
||||
$ticket = $tickets->create($user, [
|
||||
'company_id' => $validated['company_id'] ?? null,
|
||||
'subject' => $validated['subject'],
|
||||
'body' => $validated['body'],
|
||||
'priority_id' => $validated['priority_id'],
|
||||
'category_id' => $validated['category_id'] ?? null,
|
||||
'project_id' => $validated['project_id'] ?? null,
|
||||
'product_id' => $validated['product_id'] ?? null,
|
||||
'team_id' => $validated['team_id'] ?? null,
|
||||
'due_at' => $validated['due_at'] ?? null,
|
||||
], $uploadedFiles);
|
||||
|
||||
return redirect()
|
||||
->route('helpdesk.tickets.index')
|
||||
->with('success', 'Ticket '.$ticket->reference.' has been created.');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Modules\Helpdesk\Http\Controllers;
|
||||
|
||||
use Illuminate\Http\Response;
|
||||
use Modules\Helpdesk\Models\WorkflowLog;
|
||||
|
||||
final class WorkflowOpenController
|
||||
{
|
||||
public function __invoke(string $token): Response
|
||||
{
|
||||
$log = WorkflowLog::query()->where('tracking_token', $token)->first();
|
||||
|
||||
if ($log) {
|
||||
$log->forceFill([
|
||||
'first_opened_at' => $log->first_opened_at ?? now(),
|
||||
'last_opened_at' => now(),
|
||||
'opened_count' => ((int) $log->opened_count) + 1,
|
||||
])->save();
|
||||
}
|
||||
|
||||
return response(base64_decode('R0lGODlhAQABAPAAAP///wAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw=='), 200, [
|
||||
'Content-Type' => 'image/gif',
|
||||
'Cache-Control' => 'no-store, no-cache, must-revalidate, max-age=0',
|
||||
]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user