Initial Ladill Frontdesk release with deploy pipeline.
Deploy Ladill Frontdesk / deploy (push) Failing after 35s
Test / test (push) Failing after 2m45s

Visitor management app with SSO, kiosk, badges, reports, and Gitea CI deploy to frontdesk.ladill.com.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
isaacclad
2026-06-27 20:37:15 +00:00
co-authored by Cursor
commit 9e2d79936c
284 changed files with 29134 additions and 0 deletions
@@ -0,0 +1,77 @@
<?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);
}
}