Visitor management app with SSO, kiosk, badges, reports, and Gitea CI deploy to frontdesk.ladill.com. Co-authored-by: Cursor <cursoragent@cursor.com>
78 lines
2.5 KiB
PHP
78 lines
2.5 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers;
|
|
|
|
use App\Models\Organization;
|
|
use App\Models\Visit;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Http\Response;
|
|
|
|
class IcalFeedController extends Controller
|
|
{
|
|
public function __invoke(Request $request, Organization $organization): Response
|
|
{
|
|
$owner = (string) $request->query('owner');
|
|
$token = (string) $request->query('token');
|
|
|
|
abort_unless($owner !== '' && $token !== '', 403);
|
|
abort_unless(
|
|
hash_equals(
|
|
hash_hmac('sha256', "{$organization->id}:{$owner}", (string) config('app.key')),
|
|
$token,
|
|
),
|
|
403,
|
|
);
|
|
|
|
abort_unless($organization->owner_ref === $owner, 404);
|
|
|
|
$visits = Visit::owned($owner)
|
|
->where('organization_id', $organization->id)
|
|
->whereIn('status', [
|
|
Visit::STATUS_SCHEDULED,
|
|
Visit::STATUS_EXPECTED,
|
|
Visit::STATUS_WAITING,
|
|
])
|
|
->whereNotNull('scheduled_at')
|
|
->where('scheduled_at', '>=', now()->subDay())
|
|
->with(['visitor', 'host'])
|
|
->orderBy('scheduled_at')
|
|
->limit(500)
|
|
->get();
|
|
|
|
$lines = [
|
|
'BEGIN:VCALENDAR',
|
|
'VERSION:2.0',
|
|
'PRODID:-//Ladill Frontdesk//EN',
|
|
'CALSCALE:GREGORIAN',
|
|
];
|
|
|
|
foreach ($visits as $visit) {
|
|
$start = $visit->scheduled_at->utc()->format('Ymd\THis\Z');
|
|
$end = $visit->scheduled_at->copy()->addHour()->utc()->format('Ymd\THis\Z');
|
|
$summary = $this->escape('Visit: '.$visit->visitor->full_name);
|
|
$description = $this->escape(trim(($visit->purpose ?? '').' Host: '.($visit->host?->name ?? '—')));
|
|
|
|
$lines[] = 'BEGIN:VEVENT';
|
|
$lines[] = 'UID:frontdesk-visit-'.$visit->id.'@ladill.com';
|
|
$lines[] = 'DTSTAMP:'.now()->utc()->format('Ymd\THis\Z');
|
|
$lines[] = 'DTSTART:'.$start;
|
|
$lines[] = 'DTEND:'.$end;
|
|
$lines[] = 'SUMMARY:'.$summary;
|
|
$lines[] = 'DESCRIPTION:'.$description;
|
|
$lines[] = 'END:VEVENT';
|
|
}
|
|
|
|
$lines[] = 'END:VCALENDAR';
|
|
|
|
return response(implode("\r\n", $lines), 200, [
|
|
'Content-Type' => 'text/calendar; charset=utf-8',
|
|
'Content-Disposition' => 'attachment; filename="frontdesk-visits.ics"',
|
|
]);
|
|
}
|
|
|
|
protected function escape(string $value): string
|
|
{
|
|
return str_replace(["\n", ',', ';'], ['\\n', '\\,', '\\;'], $value);
|
|
}
|
|
}
|