added jquery and also fixed fontawsome

This commit is contained in:
Mark Gregory
2026-06-15 15:33:54 +01:00
parent 58e5f2e203
commit 0b2d7225cc
150 changed files with 8507 additions and 168 deletions
View File
@@ -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',
]);
}
}
View File
View File