| Server IP : 35.80.110.71 / Your IP : 216.73.216.221 Web Server : Apache/2.4.58 (Ubuntu) System : Linux ip-172-31-21-44 6.17.0-1019-aws #19~24.04.1-Ubuntu SMP Tue Jun 23 18:53:06 UTC 2026 x86_64 User : ubuntu ( 1000) PHP Version : 8.3.31 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : OFF Directory : /var/www/switchyard/current/app/Http/Controllers/PublicStatus/ |
Upload File : |
<?php
namespace App\Http\Controllers\PublicStatus;
use App\Http\Controllers\Controller;
use App\Http\Responses\ProblemResponse;
use App\Models\ClientMilestone;
use App\Models\PublicStatusToken;
use App\Models\Scopes\WorkspaceScope;
use Illuminate\Http\JsonResponse;
use Symfony\Component\HttpFoundation\Response;
/**
* Public status board endpoint.
*
* GET /public/status/{token}
*
* No bearer auth — the token IS the secret. The response is a sanitized
* subset of the project + visible milestones; raw payloads, lead scores,
* internal tasks (`is_visible = false`) are never returned.
*
* On every successful fetch we increment `view_count` and bump
* `last_viewed_at` so the operator can see usage in Filament. The
* DemoRateLimit middleware caps abuse at the IP level.
*/
class StatusController extends Controller
{
public function __invoke(string $token): Response
{
$record = PublicStatusToken::withoutGlobalScope(WorkspaceScope::class)
->where('token', $token)
->first();
if (! $record) {
return new ProblemResponse(
status: 404,
title: 'Status board not found',
detail: 'The status link is unknown — confirm the URL with whoever shared it.',
);
}
if ($record->revoked_at !== null) {
return new ProblemResponse(
status: 410,
title: 'Status link revoked',
detail: 'This link has been revoked. Ask for a fresh one.',
);
}
if ($record->expires_at !== null && $record->expires_at->isPast()) {
return new ProblemResponse(
status: 410,
title: 'Status link expired',
detail: 'This link has expired. Ask for a fresh one.',
);
}
$project = $record->clientProject()
->withoutGlobalScope(WorkspaceScope::class)
->first();
if (! $project || $project->is_archived) {
return new ProblemResponse(
status: 404,
title: 'Project not available',
detail: 'The project has been archived.',
);
}
$milestones = ClientMilestone::withoutGlobalScope(WorkspaceScope::class)
->where('client_project_id', $project->id)
->where('is_visible', true)
->orderBy('display_order')
->get();
$record->forceFill([
'view_count' => $record->view_count + 1,
'last_viewed_at' => now(),
])->save();
return new JsonResponse([
'data' => [
'project' => [
'name' => $project->name,
'client_name' => $project->client_name,
'summary' => $project->summary,
'last_synced_at' => $project->last_synced_at?->toIso8601String(),
],
'milestones' => $milestones->map(fn (ClientMilestone $m) => [
'id' => $m->id,
'name' => $m->name,
'status_name' => $m->status_name,
'status_color' => $m->status_color,
'status_type' => $m->status_type,
'is_complete' => $m->is_complete,
'due_at' => $m->due_at?->toIso8601String(),
'completed_at' => $m->completed_at?->toIso8601String(),
])->values(),
],
]);
}
}